Back to index

citadel  8.12
clientsocket.c
Go to the documentation of this file.
00001 /*
00002  * This module handles client-side sockets opened by the Citadel server (for
00003  * the client side of Internet protocols, etc.)   It does _not_ handle client
00004  * sockets for the Citadel client; for that you must look in ipc_c_tcp.c
00005  * (which, uncoincidentally, bears a striking similarity to this file).
00006  *
00007  * Copyright (c) 1987-2011 by the citadel.org team
00008  *
00009  * This program is open source software; you can redistribute it and/or
00010  * modify it under the terms of the GNU General Public License as published
00011  * by the Free Software Foundation; either version 3 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00022  */
00023 
00024 #include "sysdep.h"
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #include <stdio.h>
00028 #include <signal.h>
00029 #include <sys/types.h>
00030 #include <sys/time.h>
00031 #include <sys/socket.h>
00032 #include <netinet/in.h>
00033 #include <arpa/inet.h>
00034 #include <netdb.h>
00035 #include <string.h>
00036 #include <pwd.h>
00037 #include <errno.h>
00038 #include <stdarg.h>
00039 #include <syslog.h>
00040 #include <libcitadel.h>
00041 #include "citadel.h"
00042 #include "server.h"
00043 #ifndef HAVE_SNPRINTF
00044 #include "snprintf.h"
00045 #endif
00046 #include "sysdep_decls.h"
00047 #include "config.h"
00048 #include "clientsocket.h"
00049 #include "ctdl_module.h"
00050 
00051 int sock_connect(char *host, char *service)
00052 {
00053        struct in6_addr serveraddr;
00054        struct addrinfo hints;
00055        struct addrinfo *res = NULL;
00056        struct addrinfo *ai = NULL;
00057        int rc = (-1);
00058        int sock = (-1);
00059 
00060        if ((host == NULL) || IsEmptyStr(host))
00061               return (-1);
00062        if ((service == NULL) || IsEmptyStr(service))
00063               return (-1);
00064 
00065        memset(&hints, 0x00, sizeof(hints));
00066        hints.ai_flags = AI_NUMERICSERV;
00067        hints.ai_family = AF_UNSPEC;
00068        hints.ai_socktype = SOCK_STREAM;
00069 
00070        /*
00071         * Handle numeric IPv4 and IPv6 addresses
00072         */
00073        rc = inet_pton(AF_INET, host, &serveraddr);
00074        if (rc == 1) {                                          /* dotted quad */
00075               hints.ai_family = AF_INET;
00076               hints.ai_flags |= AI_NUMERICHOST;
00077        } else {
00078               rc = inet_pton(AF_INET6, host, &serveraddr);
00079               if (rc == 1) {                                   /* IPv6 address */
00080                      hints.ai_family = AF_INET6;
00081                      hints.ai_flags |= AI_NUMERICHOST;
00082               }
00083        }
00084 
00085        /* Begin the connection process */
00086 
00087        rc = getaddrinfo(host, service, &hints, &res);
00088        if (rc != 0) {
00089               syslog(LOG_ERR, "%s: %s", host, gai_strerror(rc));
00090               return(-1);
00091        }
00092 
00093        /*
00094         * Try all available addresses until we connect to one or until we run out.
00095         */
00096        for (ai = res; ai != NULL; ai = ai->ai_next) {
00097               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
00098               if (sock < 0) {
00099                      syslog(LOG_ERR, "socket() failed: %s", strerror(errno));
00100                      freeaddrinfo(res);
00101                      return(-1);
00102               }
00103               rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
00104               if (rc >= 0) {
00105                         freeaddrinfo(res);
00106                      return(sock);
00107               }
00108               else {
00109                      syslog(LOG_ERR, "connect() failed: %s", strerror(errno));
00110                      close(sock);
00111               }
00112        }
00113        freeaddrinfo(res);
00114        return(-1);
00115 }
00116 
00117 
00118 
00119 /*
00120  * Read data from the client socket.
00121  *
00122  * sock              socket fd to read from
00123  * buf        buffer to read into 
00124  * bytes      number of bytes to read
00125  * timeout    Number of seconds to wait before timing out
00126  *
00127  * Possible return values:
00128  *      1       Requested number of bytes has been read.
00129  *      0       Request timed out.
00130  *     -1     Connection is broken, or other error.
00131  */
00132 int socket_read_blob(int *Socket, StrBuf * Target, int bytes, int timeout)
00133 {
00134        CitContext *CCC = MyContext();
00135        const char *Error;
00136        int retval = 0;
00137 
00138 
00139        retval = StrBufReadBLOBBuffered(Target,
00140                                    CCC->SBuf.Buf,
00141                                    &CCC->SBuf.ReadWritePointer,
00142                                    Socket, 1, bytes, O_TERM, &Error);
00143        
00144        if (retval < 0) {
00145               syslog(LOG_CRIT, "socket_read_blob() failed: %s", Error);
00146        }
00147        return retval;
00148 }
00149 
00150 
00151 int CtdlSockGetLine(int *sock, StrBuf * Target, int nSec)
00152 {
00153        CitContext *CCC = MyContext();
00154        const char *Error;
00155        int rc;
00156 
00157        FlushStrBuf(Target);
00158        rc = StrBufTCP_read_buffered_line_fast(Target,
00159                                           CCC->SBuf.Buf,
00160                                           &CCC->SBuf.ReadWritePointer,
00161                                           sock, nSec, 1, &Error);
00162        if ((rc < 0) && (Error != NULL))
00163               syslog(LOG_CRIT, "CtdlSockGetLine() failed: %s", Error);
00164        return rc;
00165 }
00166 
00167 
00168 /*
00169  * client_getln()   ...   Get a LF-terminated line of text from the client.
00170  * (This is implemented in terms of client_read() and could be
00171  * justifiably moved out of sysdep.c)
00172  */
00173 int sock_getln(int *sock, char *buf, int bufsize)
00174 {
00175        int i, retval;
00176        CitContext *CCC = MyContext();
00177        const char *pCh;
00178 
00179        FlushStrBuf(CCC->sMigrateBuf);
00180        retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, 5);
00181 
00182        i = StrLength(CCC->sMigrateBuf);
00183        pCh = ChrPtr(CCC->sMigrateBuf);
00184 
00185        memcpy(buf, pCh, i + 1);
00186 
00187        FlushStrBuf(CCC->sMigrateBuf);
00188        if (retval < 0) {
00189               safestrncpy(&buf[i], "000", bufsize - i);
00190               i += 3;
00191        }
00192        return i;
00193 }
00194 
00195 
00196 /*
00197  * sock_write() - send binary to server.
00198  * Returns the number of bytes written, or -1 for error.
00199  */
00200 int sock_write(int *sock, const char *buf, int nbytes) 
00201 { return sock_write_timeout(sock, buf, nbytes, 50); }
00202 int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout)
00203 {
00204        int nSuccessLess = 0;
00205        int bytes_written = 0;
00206        int retval;
00207        fd_set rfds;
00208         int fdflags;
00209        int IsNonBlock;
00210        struct timeval tv;
00211        int selectresolution = 100;
00212 
00213        fdflags = fcntl(*sock, F_GETFL);
00214        IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
00215 
00216        while ((nSuccessLess < timeout) && 
00217               (*sock != -1) && 
00218               (bytes_written < nbytes)) 
00219        {
00220               if (IsNonBlock){
00221                      tv.tv_sec = selectresolution;
00222                      tv.tv_usec = 0;
00223                      
00224                      FD_ZERO(&rfds);
00225                      FD_SET(*sock, &rfds);
00226                      if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
00228                             close (*sock);
00229                             *sock = -1;
00230                             return -1;
00231                      }
00232               }
00233               if (IsNonBlock && !  FD_ISSET(*sock, &rfds)) {
00234                      nSuccessLess ++;
00235                      continue;
00236               }
00237               retval = write(*sock, &buf[bytes_written],
00238                             nbytes - bytes_written);
00239               if (retval < 1) {
00240                      sock_close(*sock);
00241                      *sock = -1;
00242                      return (-1);
00243               }
00244               bytes_written = bytes_written + retval;
00245               if (IsNonBlock && (bytes_written == nbytes)){
00246                      tv.tv_sec = selectresolution;
00247                      tv.tv_usec = 0;
00248                      
00249                      FD_ZERO(&rfds);
00250                      FD_SET(*sock, &rfds);
00251                      if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
00253                             close (*sock);
00254                             *sock = -1;
00255                             return -1;
00256                      }
00257               }
00258        }
00259        return (bytes_written);
00260 }
00261 
00262 
00263 
00264 /*
00265  * client_getln()   ...   Get a LF-terminated line of text from the client.
00266  * (This is implemented in terms of client_read() and could be
00267  * justifiably moved out of sysdep.c)
00268  */
00269 int sock_getln_err(int *sock, char *buf, int bufsize, int *rc, int nSec)
00270 {
00271        int i, retval;
00272        CitContext *CCC = MyContext();
00273        const char *pCh;
00274 
00275        FlushStrBuf(CCC->sMigrateBuf);
00276        *rc = retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, nSec);
00277 
00278        i = StrLength(CCC->sMigrateBuf);
00279        pCh = ChrPtr(CCC->sMigrateBuf);
00280 
00281        memcpy(buf, pCh, i + 1);
00282 
00283        FlushStrBuf(CCC->sMigrateBuf);
00284        if (retval < 0) {
00285               safestrncpy(&buf[i], "000", bufsize - i);
00286               i += 3;
00287        }
00288        return i;
00289 }
00290 
00291 /*
00292  * Multiline version of sock_gets() ... this is a convenience function for
00293  * client side protocol implementations.  It only returns the first line of
00294  * a multiline response, discarding the rest.
00295  */
00296 int ml_sock_gets(int *sock, char *buf, int nSec)
00297 {
00298        int rc = 0;
00299        char bigbuf[1024];
00300        int g;
00301 
00302        g = sock_getln_err(sock, buf, SIZ, &rc, nSec);
00303        if (rc < 0)
00304               return rc;
00305        if (g < 4)
00306               return (g);
00307        if (buf[3] != '-')
00308               return (g);
00309 
00310        do {
00311               g = sock_getln_err(sock, bigbuf, SIZ, &rc, nSec);
00312               if (rc < 0)
00313                      return rc;
00314               if (g < 0)
00315                      return (g);
00316        } while ((g >= 4) && (bigbuf[3] == '-'));
00317 
00318        return (strlen(buf));
00319 }
00320 
00321 
00322 /*
00323  * sock_puts() - send line to server - implemented in terms of serv_write()
00324  * Returns the number of bytes written, or -1 for error.
00325  */
00326 int sock_puts(int *sock, char *buf)
00327 {
00328        int i, j;
00329 
00330        i = sock_write(sock, buf, strlen(buf));
00331        if (i < 0)
00332               return (i);
00333        j = sock_write(sock, "\n", 1);
00334        if (j < 0)
00335               return (j);
00336        return (i + j);
00337 }