1 /*
2  * Some code in this file is derived from the public domain code in
3  *              WWW/Library/Implementation/HTTCP.c distributed with lynx-2.2,
4  *              whose original author is Tim Berners-lee <timbl@info.cern.ch>.
5  *
6  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
7  *
8  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
9  *
10  * This file may be distributed under the terms of the Q Public License
11  * as defined by Trolltech AS of Norway and appearing in the file
12  * LICENSE.QPL included in the packaging of this file.
13  *
14  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
15  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
17  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
18  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
19  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
20  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/tcp.c,v 1.6 2011/05/16 16:21:59 william Exp $
23  */
24 
25 #define _INCLUDE_FROM_TCP_C_
26 
27 #include "tgifdefs.h"
28 
29 #include "msg.e"
30 #include "remote.e"
31 #include "strtbl.e"
32 #include "tcp.e"
33 #include "util.e"
34 
SetSocketBlockingState(pn_socket,n_blocking)35 void SetSocketBlockingState(pn_socket, n_blocking)
36    int *pn_socket, n_blocking;
37 {
38    int rc;
39 #ifdef O_NONBLOCK
40    int flags = fcntl(*pn_socket, F_GETFL);
41 
42    rc = fcntl(*pn_socket, F_SETFL,
43             n_blocking ? (flags & (~O_NONBLOCK)) : (flags | O_NONBLOCK));
44 #else /* ~O_NONBLOCK */
45    int val=(!n_blocking);
46 
47    rc = ioctl(*pn_socket, FIONBIO, &val);
48 #endif /* O_NONBLOCK */
49    if (rc == (-1)) {
50       fprintf(stderr, "%s\n",
51             TgLoadString(n_blocking ? STID_FAIL_TO_MAKE_SOCKET_BLOCK :
52             STID_FAIL_TO_MAKE_SOCKET_NON_BLOCK));
53    }
54 }
55 
TcpFreeBuf(buf)56 void TcpFreeBuf(buf)
57    char *buf;
58 {
59    free(buf);
60 }
61 
62 static int gnPipeBroken=FALSE;
63 
64 static
BrokenPipe(nSig)65 void BrokenPipe(nSig)
66    int nSig;
67 {
68    if (nSig == SIGPIPE) {
69       gnPipeBroken = TRUE;
70       signal(SIGPIPE, SIG_DFL);
71    }
72 }
73 
TcpDoConnect(psz_host,us_port,pn_socket)74 int TcpDoConnect(psz_host, us_port, pn_socket)
75    char *psz_host;
76    int us_port, *pn_socket;
77 {
78    static int not_initialized=TRUE;
79    struct sockaddr_in soc_address;
80    struct sockaddr_in *sin=(&soc_address);
81    struct hostent *p_hostent=NULL;
82    int status=TG_REMOTE_STATUS_OK;
83 
84    if (not_initialized) {
85       not_initialized = FALSE;
86       signal(SIGPIPE, BrokenPipe);
87    }
88    if (*psz_host >= '0' && *psz_host <= '9') {
89       sin->sin_addr.s_addr = inet_addr(psz_host);
90    } else {
91       p_hostent = gethostbyname(psz_host);
92       if (p_hostent == NULL) {
93          return TG_REMOTE_STATUS_HOST;
94       }
95       memcpy(&sin->sin_addr, p_hostent->h_addr, p_hostent->h_length);
96    }
97    sin->sin_family = AF_INET;
98    sin->sin_port = htons((unsigned short)us_port);
99    *pn_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
100 
101    SetSocketBlockingState(pn_socket, FALSE);
102 
103    status = connect(*pn_socket, (struct sockaddr*)&soc_address,
104          sizeof(soc_address));
105 #ifdef EAGAIN
106    if ((status < 0) && (errno==EINPROGRESS || errno==EAGAIN)) {
107 #else /* ~EAGAIN */
108    if ((status < 0) && (errno == EINPROGRESS)) {
109 #endif /* EAGAIN */
110       struct timeval timeout;
111       int rc=0;
112 
113       timeout.tv_sec = 0;
114       timeout.tv_usec = 100000;
115       while (rc <= 0) {
116          fd_set writefds;
117 
118          FD_ZERO(&writefds);
119          FD_SET(*pn_socket, &writefds);
120 #ifdef __hpux
121          rc = select(FD_SETSIZE, NULL, (int*)&writefds, NULL, &timeout);
122 #else /* ~__hpux */
123          rc = select(FD_SETSIZE, NULL, &writefds, NULL, &timeout);
124 #endif /* __hpux */
125          if ((rc < 0)&&(errno != EALREADY)) {
126             status = rc;
127             break;
128          } else if (rc > 0) {
129             gnPipeBroken = FALSE;
130             status = connect(*pn_socket, (struct sockaddr*)&soc_address,
131                   sizeof(soc_address));
132             if (gnPipeBroken) {
133                fprintf(stderr, TgLoadString(STID_BROKEN_PIPE_CONTACT_HOST),
134                      psz_host);
135                fprintf(stderr, "\n");
136             }
137             if ((status < 0)&&(errno == EISCONN)) status = TG_REMOTE_STATUS_OK;
138             if (errno == EALREADY) {
139                rc = 0;
140             } else {
141                break;
142             }
143          } else {
144             status = connect(*pn_socket, (struct sockaddr*)&soc_address,
145                   sizeof(soc_address));
146 #ifdef EAGAIN
147             if ((status < 0) && (errno != EALREADY) && (errno != EISCONN) &&
148                   (errno != EAGAIN))
149 #else /* ~EAGAIN */
150             if ((status < 0) && (errno != EALREADY) && (errno != EISCONN))
151 #endif /* EAGAIN */
152                break;
153          }
154          if (UserAbortComm()) {
155             status = TG_REMOTE_STATUS_INTR;
156             errno = EINTR;
157             break;
158          }
159       }
160    }
161    if (status >= 0) {
162       SetSocketBlockingState(pn_socket, TRUE);
163    } else {
164       close(*pn_socket);
165    }
166    return status;
167 }
168 
TcpDoWrite(n_socket,buf,buf_sz)169 int TcpDoWrite(n_socket, buf, buf_sz)
170    int n_socket, buf_sz;
171    char *buf;
172 {
173    int status=0;
174 
175    if (buf == NULL) return TG_REMOTE_STATUS_OK;
176 
177    status = write(n_socket, buf, (int)buf_sz);
178    if (status <= 0) {
179       if (status == 0) {
180          fprintf(stderr, "%s\n", TgLoadString(STID_WRITE_TO_SOCKET_FAILED));
181       } else if ((errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE)) {
182          fprintf(stderr, "%s\n",
183                TgLoadString(STID_UNEXP_NETWORK_ERR_WRITE_SOCK));
184          return TG_REMOTE_STATUS_WRITE;
185       }
186    }
187    return TG_REMOTE_STATUS_OK;
188 }
189 
190 #define MIN_READ_SIZE 0x100
191 
TcpDoRead(n_socket,ppsz_buf,pn_buf_sz)192 int TcpDoRead(n_socket, ppsz_buf, pn_buf_sz)
193    int n_socket, *pn_buf_sz;
194    char **ppsz_buf;
195 {
196    int buf_sz=0x400, len=0, end_of_file=FALSE;
197    char *buf=(char*)malloc(buf_sz*sizeof(char));
198 
199    if (pn_buf_sz != NULL) *pn_buf_sz = 0;
200    *ppsz_buf = NULL;
201    if (buf == NULL) {
202       FailAllocMessage();
203       return TG_REMOTE_STATUS_MEM;
204    }
205    do {
206       int bytes_read;
207 
208       if (buf_sz - len < MIN_READ_SIZE) {
209          buf_sz += 0x400;
210          if ((buf=(char*)realloc(buf, buf_sz)) == NULL) {
211             FailAllocMessage();
212             return TG_REMOTE_STATUS_MEM;
213          }
214       }
215       bytes_read = read(n_socket, &buf[len], buf_sz-len-1);
216       if (bytes_read <= 0) {
217          if (bytes_read < 0 && (errno == ENOTCONN || errno == ECONNRESET ||
218                errno == EPIPE)) {
219             free(buf);
220             fprintf(stderr, "%s\n",
221                   TgLoadString(STID_READ_FROM_SOCKET_FAILED));
222             return TG_REMOTE_STATUS_READ;
223          } else if (bytes_read < 0) {
224             free(buf);
225             fprintf(stderr, "%s\n",
226                   TgLoadString(STID_UNEXP_NETWORK_ERR_READ_SOCK));
227             return TG_REMOTE_STATUS_NET;
228          }
229          end_of_file = TRUE;
230       } else {
231          len += bytes_read;
232       }
233    } while (!end_of_file);
234    buf[len] = '\0';
235    *ppsz_buf = buf;
236    if (pn_buf_sz != NULL) *pn_buf_sz = len;
237    return TG_REMOTE_STATUS_OK;
238 }
239 
240