## modules/sk/sk_socket.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. SK_atoport
2. SK_close
3. SK_getsock
4. SK_accept_connection
5. SK_connect
6. SK_read
7. SK_write
8. SK_gets
9. SK_puts
10. SK_putc
11. SK_getc
12. SK_getpeername
13. SK_getpeerip

       1 /***************************************
       2   $Revision: 1.20 $
       3
       4   Socket module - routines facilitating calls to socket library.
       5
       6   Status: NOT REVUED, TESTED
       7
       8   Basic code adapted by Chris Ottrey from
       9       http://www.ibrado.com/sock-faq/sfaq.html#faq65 - sample source code.
      10   ******************/ /******************
      11   Modification History:
      12         ottrey (08/03/1999) Created from sockhelp.c.
      13         ottrey (08/03/1998) Heavily butchered.
      14         joao   (22/06/1999) Modified socket creation and accepts.
      15         marek  (December 2000) Added connection function w/timeout.
      16   ******************/ /******************
      17   Copyright (c) 1999,2000,2001,2002                     RIPE NCC
      18
      19   All Rights Reserved
      20
      21   Permission to use, copy, modify, and distribute this software and its
      22   documentation for any purpose and without fee is hereby granted,
      23   provided that the above copyright notice appear in all copies and that
      24   both that copyright notice and this permission notice appear in
      25   supporting documentation, and that the name of the author not be
      26   used in advertising or publicity pertaining to distribution of the
      27   software without specific, written prior permission.
      28
      29   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
      30   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
      31   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      32   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
      33   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      34   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      35   ***************************************/
      36
      37 #include "rip.h"
      38
      39 /* SK_atoport() */
      40 /*++++++++++++++++++++++++++++++++++++++
      41    Take a service name, and a service type, and return a port number.  If the
      42    service name is not found, it tries it as a decimal number.  The number
      43    returned is byte ordered for the network.
      44
      45   char *service   Service name (or port number).
      46
      47   char *proto     Protocol (eg "tcp").
      48
      49   Author:
      50         ottrey.
      51
      52   ++++++++++++++++++++++++++++++++++++++*/
      53 int SK_atoport(const char *service, const char *proto) {
         /* [<][>][^][v][top][bottom][index][help] */
      54   unsigned port;
      55   long int lport;
      56   struct servent *serv;
      57   char *errpos;
      58   struct servent result;
      59   char buffer[STR_XXL];
      60
      61   /* First try to read it from /etc/services */
      62
      63 #ifdef __linux__
      64   if(getservbyname_r(service, proto, &result, buffer, sizeof(buffer), &serv) < 0) serv = NULL;
      65 #else
      66   serv = getservbyname_r(service, proto, &result, buffer, sizeof(buffer));
      67 #endif
      68
      69   if (serv != NULL)
      70     port = serv->s_port;
      71   else { /* Not in services, maybe a number? */
      72     lport = strtol(service,&errpos,0);
      73     if ( (errpos[0] != 0) || (lport < 1) || (lport > 65535) )
      74       return -1; /* Invalid port address */
      75     port = htons(lport);
      76   }
      77   return port;
      78 } /* SK_atoport() */
      79
      80
      81 /* SK_close() */
      82 /*++++++++++++++++++++++++++++++++++++++
      83
      84   int SK_close     wrapper around closing the socket. Returns the value
      85                    returned by close(2)
      86
      87   int socket       socket to be closed
      88
      89   Author:
      90         ottrey
      91   ++++++++++++++++++++++++++++++++++++++*/
      92 int SK_close(int socket) {
         /* [<][>][^][v][top][bottom][index][help] */
      93   ER_dbg_va(FAC_SK, ASP_SK_GEN, "Closing socket... %d", socket);
      94
      95   return close(socket);
      96 }
      97
      98 /* SK_getsock() */
      99 /*++++++++++++++++++++++++++++++++++++++
     100
     101    int  SK_getsock        This function creates a socket and binds to it.
     102                           Returns the number of the created
     103                           descriptor/listening socket.
     104
     105    int socket_type        SOCK_STREAM or SOCK_DGRAM (TCP or UDP sockets)
     106
     107    unsigned  port         The port to listen on. Ports < 1024 are
     108                           reserved for the root user. Host byte order.
     109
     110    int backlog            Size of the backlog queue to be set on that
     111                           socket.
     112
     113    uint32_t bind_address  Address to bind to, in network order.
     114
     115   Authors:
     116         ottrey,
     117         joao,
     118         marek (added htons conversion for port).
     119
     120   ++++++++++++++++++++++++++++++++++++++*/
     121 int  SK_getsock(int socket_type, unsigned h_port, int backlog,
         /* [<][>][^][v][top][bottom][index][help] */
     122                 uint32_t bind_address) {
     123   struct sockaddr_in address;
     124   int listening_socket;
     125   int reuse_addr = 1;
     126   u_short port = htons(h_port);
     127
     128   /* Setup internet address information.
     129      This is used with the bind() call */
     130   memset((char *) &address, 0, sizeof(address));
     131   address.sin_family = AF_INET;
     132   address.sin_port = port;
     133   address.sin_addr.s_addr = bind_address;
     134
     135   /* Map all of the signals and exit routine */
     136
     137   listening_socket = socket(AF_INET, socket_type, 0);
     138   if (listening_socket < 0) {
     139     perror("socket");
     140     exit(EXIT_FAILURE);
     141   }
     142
     143   setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse_addr, sizeof(reuse_addr));
     144
     145   if (bind(listening_socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
     146     perror("bind");
     147     close(listening_socket);
     148     exit(EXIT_FAILURE);
     149   }
     150
     151
     152   if (socket_type == SOCK_STREAM) {
     153     listen(listening_socket, backlog); /* Queue up to five connections before
     154                                   having them automatically rejected. */
     155   }
     156
     157   return listening_socket;
     158 } /* SK_getsock() */
     159
     160 /*++++++++++++++++++++++++++++++++++++++
     161
     162    Wait for an incoming connection on the specified socket
     163
     164    int  SK_accept_connection The socket for communicating to the client
     165
     166    int  listening_socket     The socket that the server is bound to
     167
     168   Authors:
     169         joao,
     170         marek.
     171   ++++++++++++++++++++++++++++++++++++++*/
     172 int SK_accept_connection(int listening_socket) {
         /* [<][>][^][v][top][bottom][index][help] */
     173   int connected_socket = -1;
     174   int num_errors = 0;
     175
     176 #define MAX_ACCEPT_ERRORS 3
     177
     178   for (;;) {
     179
     180     ER_dbg_va(FAC_SK, ASP_SK_GEN,
     181               "Going to accept connections on socket : %d",listening_socket);
     182
     183     connected_socket = accept(listening_socket, NULL, NULL);
     184     if (connected_socket < 0) {
     185       /* Either a real error occured, or blocking was interrupted for
     186          some reason.  Only abort execution if a real error occured. */
     187       switch(errno) {
     188       case EINTR:        /* Interrupted system call */
     189       case ECONNABORTED: /* Software caused connection abort */
     190         /* no warning */
     191         continue;    /* don't return - do the accept again */
     192       default:
     193         /* special case: shutdown of the server - just return */
     194         if( CO_get_do_server() == 0 ) {
     195           return -1;
     196         }
     197         else { /* real error */
     198           if( ++num_errors < MAX_ACCEPT_ERRORS ) {
     199             /* warn */
     200             ER_perror(FAC_SK, SK_ACERW, "(%d) %s", errno, strerror(errno));
     201           }
     202           else {
     203             /* crash */
     204             ER_perror(FAC_SK, SK_ACERF,
     205                 "too many accept() errors (maximum is %d)", MAX_ACCEPT_ERRORS);
     206             die;
     207           }
     208         }
     209       }
     210     }
     211     else { /* success */
     212        ER_dbg_va(FAC_SK, ASP_SK_GEN, "client connected on socket %d",
     213             connected_socket
     214             );
     215
     216        return connected_socket;
     217     }
     218
     219   }
     220 }
     221
     222
     223 /*++++++++++++++++++++++++++++++++++++++
     224
     225   er_ret_t SK_connect       wrapper around connect(), doing non-blocking
     226                             connection with timeout
     227
     228   int *sock         pointer to the storage for socket descriptor
     229
     230   char *hostname    host to connect to
     231
     232   int port          port to connect to
     233
     234   int timeout       in seconds
     235
     236   Author: marek
     237
     238   ++++++++++++++++++++++++++++++++++++++*/
     239 er_ret_t SK_connect(int  *sock, char *hostname, unsigned int port, unsigned int timeout)
         /* [<][>][^][v][top][bottom][index][help] */
     240 {
     241   struct sockaddr_in sin;
     242   struct hostent *hp;
     243   int s;
     244   int flags;
     245   struct timeval ptm;
     246   fd_set rset, wset;
     247   int gs, sel, er, erlen=sizeof(er);
     248   int error;
     249   struct hostent result;
     250   char aliasbuf[8192]; /* Stevens, UNIX net. prog., p.304 */
     251
     252   /* look up the host name */
     253 #ifdef __linux__
     254   er = (gethostbyname_r(hostname,  &result, aliasbuf,
     255                         sizeof(aliasbuf), &hp, &error) < 0 );
     256 #else /* default is Solaris implementation */
     257   hp = gethostbyname_r(hostname,  &result, aliasbuf,
     258                        sizeof(aliasbuf), &error);
     259   er = ( hp == NULL );
     260 #endif
     261
     262   if( er ) {
     263     return SK_BADHOST;
     264   }
     265
     266   /* create a socket */
     267   s = socket(AF_INET, SOCK_STREAM, 0);
     268   if (s < 0) {
     269     return SK_SOCKET;
     270   }
     271
     272   /* bind to it */
     273   bzero((caddr_t)&sin, sizeof (sin));
     274   sin.sin_family = hp->h_addrtype;
     275   if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
     276     close(s);
     277     return SK_BIND;
     278   }
     279   bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
     280   sin.sin_port=htons(port);
     281
     282   /* connect in non-blocking mode */
     283   flags = fcntl(s, F_GETFL, 0);
     284   fcntl(s, F_SETFL, flags | O_NONBLOCK );
     285
     286   if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0
     287       && errno != EINPROGRESS ) {
     288     close(s);
     289     return SK_CONNECT;
     290   }
     291
     292   /* now wait for success */
     293   FD_ZERO( &rset );
     294   FD_SET( s, &rset );
     295   wset = rset;
     296   ptm.tv_usec = 0;
     297   ptm.tv_sec = timeout;
     298
     299   if( (sel=select(s+1,  &rset, &wset, NULL, &ptm)) == 0 ) {
     300     /* timeout */
     301     close(s);
     302     return SK_TIMEOUT;
     303   }
     304   if (sel < 0) {
     305     close(s);
     306     return SK_CONNECT;
     307   }
     308
     309   gs = getsockopt(s, SOL_SOCKET, SO_ERROR, &er, &erlen);
     310
     311   if( gs < 0 || er ) {   /* Stevens code, p.411 is exceptionally crappy */
     312     close(s);
     313     return SK_CONNECT;
     314   } /* if error */
     315
     316   fcntl(s, F_SETFL, flags);
     317   *sock = s;
     318
     319   return SK_OK;
     320 }
     321
     322
     323 /* SK_read() */
     324 /*++++++++++++++++++++++++++++++++++++++
     325
     326    This is just like the read() system call, except that it will make
     327    sure that all your data goes through the socket.
     328
     329    int    SK_read   Returns the number of bytes read.
     330
     331    int    sockfd    The socket file descriptor.
     332
     333    char   *buf      The buffer to be read from the socket.
     334
     335    size_t count     The number of bytes in the buffer.
     336
     337   Author:
     338         ottrey
     339
     340   ++++++++++++++++++++++++++++++++++++++*/
     341 int SK_read(int sockfd, char *buf, size_t count) {
         /* [<][>][^][v][top][bottom][index][help] */
     342   size_t bytes_read = 0;
     343   int this_read;
     344
     345   while (bytes_read < count) {
     346     do
     347       this_read = read(sockfd, buf, count - bytes_read);
     348     while ( (this_read < 0) && (errno == EINTR) );
     349     if (this_read < 0)
     350       return this_read;
     351     else if (this_read == 0)
     352       return bytes_read;
     353     bytes_read += this_read;
     354     buf += this_read;
     355   }
     356
     357   return count;
     358
     359 } /* SK_read() */
     360
     361
     362 /* SK_write() */
     363 /*++++++++++++++++++++++++++++++++++++++
     364
     365    int SK_write   Returns:
     366                      -1 on error
     367                       0 on timeout
     368                       1 on success (all bytes written)
     369
     370    int sockfd                      The socket file descriptor.
     371
     372    const char *buf                 The buffer to be written to the socket.
     373
     374    int count                       The number of bytes in the buffer.
     375
     376    const struct timeval *timeout   Maximum time to wait between each
     377                                    write() call, or NULL for "forever".
     378
     379    int *count_sent                 Set to the number of bytes sucessfully
     380                                    written.  This value is always valid,
     381                                    although it will be less than the
     382                                    total number of bytes to send if
     383                                    the function returns -1 or 0.  NULL
     384                                    may be sent if the caller does not
     385                                    care about the number of bytes sent on
     386                                    failure.
     387
     388   ++++++++++++++++++++++++++++++++++++++*/
     389
     390 int
     391 SK_write(int sockfd,
         /* [<][>][^][v][top][bottom][index][help] */
     392          const char *buf,
     393          int count,
     394          const struct timeval *timeout,
     395          int *count_sent)
     396 {
     397   int     local_count_sent;    /* only used if caller passes NULL */
     398   fd_set  fds;
     399   int     select_ret;
     400   struct  timeval tv;
     401   int     write_ret;
     402
     403   /* copious logging never hurt anybody (well, except people with slow
     404      computers or small hard disks) */
     405   ER_dbg_va(FAC_SK, ASP_SK_WRIT,
     406             "SK_write = { sockfd=[%d], buf=[%s], count=[%d]",
     407             sockfd, buf, count);
     408
     409   /* allow caller to pass NULL if it doesn't care about the count sent */
     410   if (count_sent == NULL) {
     411       count_sent = &local_count_sent;
     412   }
     413
     414   /* loop around until we send all of our data, or we get a timeout or
     415      disconnect */
     416   *count_sent = 0;
     417   while (*count_sent < count) {
     418
     419     /* first, call select() and see if we can write without blocking */
     420     FD_ZERO(&fds);
     421     FD_SET(sockfd, &fds);
     422     if (timeout == NULL) {
     423         select_ret = select(sockfd+1, NULL, &fds, NULL, NULL);
     424     } else {
     425         /* make a copy of timeout, because it's value is undefined
     426            on return from select() */
     427         tv = *timeout;
     428         select_ret = select(sockfd+1, NULL, &fds, NULL, &tv);
     429     }
     430     /* check for error */
     431     if (select_ret < 0) {
     432         /* log the error and return "timeout" */
     433         ER_perror(FAC_SK, SK_SELECT, "(%d) %s", errno, strerror(errno));
     434         return -1;
     435     }
     436     /* check for timeout */
     437     if ((select_ret == 0) || !FD_ISSET(sockfd, &fds)) {
     438         return -1;
     439     }
     440
     441
     442     /* at this point we can safely write */
     443     write_ret = write(sockfd, buf, count - *count_sent);
     444
     445     /* if write failed, assume other side disconnected */
     446     if (write_ret <= 0) {
     447         return 0;
     448     }
     449
     450     /* update our current status */
     451     *count_sent += write_ret;
     452     buf += write_ret;
     453
     454   }
     455
     456   /* all bytes sent, return success */
     457   return 1;
     458 } /* SK_write() */
     459
     460
     461 /* SK_gets() */
     462 /*++++++++++++++++++++++++++++++++++++++
     463
     464    This function reads from a socket, until it recieves a linefeed
     465    character.  It fills the buffer "str" up to the maximum size "count".
     466
     467    int SK_gets      Returns the total_count of bytes read.
     468
     469    int    sockfd    The socket file descriptor.
     470
     471    char   *str      The buffer to be written from the socket.
     472
     473    size_t count     The number of bytes in the buffer.
     474
     475
     476   Authors:
     477         ottrey,
     478         marek (modified for meaningful error codes).
     479
     480   Side Effects:
     481         This function will return -1 if the socket is closed during the read operation.
     482
     483         Note that if a single line exceeds the length of count, the extra data
     484         will be read and discarded!  You have been warned.
     485
     486   ++++++++++++++++++++++++++++++++++++++*/
     487 int SK_gets(int  sockfd, char *str, size_t count) {
         /* [<][>][^][v][top][bottom][index][help] */
     488   int bytes_read;
     489   int total_count = 0;
     490   char *current_position;
     491   char last_read = 0;
     492
     493   int control_c = 0;
     494
     495   current_position = str;
     496   while (last_read != 10) {
     497
     498     bytes_read = read(sockfd, &last_read, 1);
     499     if (bytes_read <= 0) {
     500       /* The other side may have closed unexpectedly */
     501       return SK_DISCONNECT;
     502       /* Is this effective on other platforms than linux? */
     503     }
     504     if ( (total_count < count) && (last_read != 10) && (last_read !=13) ) {
     505       *current_position = last_read;
     506       current_position++;
     507       total_count++;
     508     }
     509
     510     if (last_read == -1) {
     511       bytes_read = read(sockfd, &last_read, 1);
     512       if (last_read == -12) {
     513         ER_dbg_va(FAC_SK, ASP_SK_GEN,"Client pressed Control-c");
     514         control_c = 1;
     515         ER_dbg_va(FAC_SK, ASP_SK_GEN,"returning SK_INTERRUPT");
     516         return SK_INTERRUPT;
     517       }
     518     }
     519   }
     520   if (count > 0) {
     521     *current_position = 0;
     522   }
     523
     524   return total_count;
     525
     526 } /* SK_gets() */
     527
     528
     529 /* SK_puts() */
     530 /*++++++++++++++++++++++++++++++++++++++
     531
     532    This function writes a character string out to a socket.
     533
     534    int SK_puts  The total_count of bytes written,
     535                 or -1 on hangup, error, or timeout
     536
     537    int    sockfd    The socket file descriptor.
     538
     539    char   *str      The buffer to be written from the socket.
     540
     541    const struct timeval *timeout   Maximum time to wait between each
     542                                    write() call, or NULL for "forever".
     543
     544   ++++++++++++++++++++++++++++++++++++++*/
     545 int
     546 SK_puts(int sockfd, const char *str, const struct timeval *timeout)
         /* [<][>][^][v][top][bottom][index][help] */
     547 {
     548   int count_sent;
     549   if (SK_write(sockfd, str, strlen(str), timeout, &count_sent) <= 0) {
     550       return -1;
     551   } else {
     552       return count_sent;
     553   }
     554 } /* SK_puts() */
     555
     556 /* SK_putc() */
     557 /*++++++++++++++++++++++++++++++++++++++
     558
     559   This function writes a single character out to a socket.
     560
     561    int SK_putc       Returns the number of characters written.
     562
     563    int sockfd        socket
     564
     565    char ch           character
     566
     567    const struct timeval *timeout   Maximum time to wait between each
     568                                    write() call, or NULL for "forever".
     569
     570   ++++++++++++++++++++++++++++++++++++++*/
     571 int
     572 SK_putc(int sockfd, char ch, const struct timeval *timeout)
         /* [<][>][^][v][top][bottom][index][help] */
     573 {
     574   int count_sent;
     575   SK_write(sockfd, &ch, 1, timeout, &count_sent);
     576   return count_sent;
     577 }/* SK_putc() */
     578
     579 /*++++++++++++++++++++++++++++++++++++++
     580
     581    This function reads a single character from a socket.
     582
     583    returns EOF when no character can be read.
     584
     585   ++++++++++++++++++++++++++++++++++++++*/
     586 int SK_getc(int  sockfd) {
         /* [<][>][^][v][top][bottom][index][help] */
     587   char ch;
     588
     589   if( read(sockfd, &ch, 1) <= 0 ) {
     590     return EOF;
     591   }
     592   else {
     593     return ch;
     594   }
     595 }/* SK_getc() */
     596
     597 /* SK_getpeername() */
     598 /*++++++++++++++++++++++++++++++++++++++
     599
     600   This function will tell you who is at the other end of a connected stream socket.
     601
     602   char *SK_getpeername     Returns allocated string with the IP in it,
     603                            or "--" if the descriptor is not a socket,
     604                            or NULL on error.
     605
     606   int    sockfd            The socket or file descriptor.
     607
     608   +html+ <PRE>
     609   Authors:
     610         ottrey,
     611         marek (modified error handling, made MT-Safe).
     612   +html+ </PRE>
     613
     614   ++++++++++++++++++++++++++++++++++++++*/
     615 char *SK_getpeername(int  sockfd)
         /* [<][>][^][v][top][bottom][index][help] */
     616 {
     617   char *hostaddress=NULL;
     618   struct sockaddr_in addr_in;
     619   int namelen=sizeof(addr_in);
     620
     621   if (getpeername(sockfd, (struct sockaddr *)&addr_in, &namelen) == 0) {
     622
     623     hostaddress = (char *)UT_malloc(INET_ADDRSTRLEN);
     624     inet_ntop(AF_INET, &(addr_in.sin_addr),  hostaddress, INET_ADDRSTRLEN);
     625   }
     626   else {
     627     int er = errno;
     628
     629     if( er == ENOTSOCK ) {
     630       hostaddress = wr_string("--");
     631     }
     632     else {
     633       /* XXX: hack to avoid crash in the case where peer disconnects */
     634       /* To fix this, the socket interface needs to deal with a structure
     635          containing not only the file descriptor, but also the address of
     636          the host and the peer.  Other goodies, like the current time,
     637          time of last operation, and last errno could also be included
     638          for good measure. */
     639       hostaddress = UT_strdup("127.0.0.1");
     640     }
     641   }
     642
     643   return hostaddress;
     644
     645 } /* SK_getpeername() */
     646
     647
     648 /* SK_getpeerip */
     649 /*++++++++++++++++++++++++++++++++++++++
     650
     651   This function will check the ip of the connected peer and store it in the
     652   ip_addr_t structure defined in the IP module.
     653
     654   int SK_getpeerip    returns 0 on success, -1 on failure.
     655
     656   int  sockfd         The socket descriptor (file will result in -1)
     657
     658   ip_addr_t *ip       Pointer to where the address should be stored.
     659
     660   +html+ <PRE>
     661   Author:
     662         marek
     663   +html+ </PRE>
     664   ++++++++++++++++++++++++++++++++++++++*/
     665
     666 int SK_getpeerip(int  sockfd, ip_addr_t *ip) {
         /* [<][>][^][v][top][bottom][index][help] */
     667   struct sockaddr_in addr_in;
     668   int namelen=sizeof(addr_in);
     669   int ret=-1;
     670
     671   memset(& addr_in, 0, sizeof(struct sockaddr_in));
     672
     673   if (getpeername(sockfd, (struct sockaddr *)(& addr_in), &namelen) != -1) {
     674     ret=0;
     675     IP_addr_s2b(ip, &addr_in, namelen);
     676   } else {
     677       /* XXX: hack to avoid crash in the case where peer disconnects */
     678       /* To fix this, the socket interface needs to deal with a structure
     679          containing not only the file descriptor, but also the address of
     680          the host and the peer.  Other goodies, like the current time,
     681          time of last operation, and last errno could also be included
     682          for good measure. */
     683       ret = 0;
     684       addr_in.sin_family = AF_INET;
     685       addr_in.sin_addr.s_addr = INADDR_LOOPBACK;
     686       addr_in.sin_port = 0;
     687       IP_addr_s2b(ip, &addr_in, namelen);
     688   }
     689
     690   return ret;
     691 }
     692

/* [<][>][^][v][top][bottom][index][help] */