1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
4 */
5
6 /*
7 * Program: MS-DOS TCP/IP routines
8 *
9 * Author: Mark Crispin
10 *
11 * Date: 11 April 1989
12 * Last Edited: 3 April 2010
13 *
14 * Previous versions of this file were:
15 *
16 * Copyright 1988-2008 University of Washington
17 *
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 */
24
25 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
26 static long ttmo_read = 0; /* TCP timeouts, in seconds */
27 static long ttmo_write = 0;
28
29 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
30 long *contd);
31
32 /* TCP/IP manipulate parameters
33 * Accepts: function code
34 * function-dependent value
35 * Returns: function-dependent return value
36 */
37
tcp_parameters(long function,void * value)38 void *tcp_parameters (long function,void *value)
39 {
40 void *ret = NIL;
41 switch ((int) function) {
42 case SET_TIMEOUT:
43 tmoh = (tcptimeout_t) value;
44 case GET_TIMEOUT:
45 ret = (void *) tmoh;
46 break;
47 case SET_READTIMEOUT:
48 ttmo_read = (long) value;
49 case GET_READTIMEOUT:
50 ret = (void *) ttmo_read;
51 break;
52 case SET_WRITETIMEOUT:
53 ttmo_write = (long) value;
54 case GET_WRITETIMEOUT:
55 ret = (void *) ttmo_write;
56 break;
57 }
58 return ret;
59 }
60
61 /* TCP/IP open
62 * Accepts: host name
63 * contact service name
64 * contact port number
65 * Returns: TCP/IP stream if success else NIL
66 */
67
tcp_open(char * host,char * service,unsigned long port)68 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
69 {
70 TCPSTREAM *stream = NIL;
71 struct sockaddr_in sin;
72 int sock;
73 char *s,tmp[MAILTMPLEN];
74 port &= 0xffff; /* erase flags */
75 /* The domain literal form is used (rather than simply the dotted decimal
76 as with other Unix programs) because it has to be a valid "host name"
77 in mailsystem terminology. */
78 sin.sin_family = AF_INET; /* family is always Internet */
79 /* look like domain literal? */
80 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
81 strcpy (tmp,host+1); /* yes, copy number part */
82 tmp[strlen (tmp)-1] = '\0';
83 if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
84 sprintf (tmp,"Bad format domain-literal: %.80s",host);
85 mm_log (tmp,ERROR);
86 return NIL;
87 }
88 }
89 /* look up host name */
90 else if (!lookuphost (&host,&sin)) {
91 sprintf (tmp,"Host not found: %s",host);
92 mm_log (tmp,ERROR);
93 return NIL;
94 }
95
96 /* copy port number in network format */
97 if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
98 /* get a TCP stream */
99 if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
100 sprintf (tmp,"Unable to create TCP socket (%d)",errno);
101 mm_log (tmp,ERROR);
102 fs_give ((void **) &host);
103 return NIL;
104 }
105 #if 0
106 /* needed? */
107 else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
108 sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
109 sock,FD_SETSIZE);
110 close (sock);
111 errno = ENOBUFS; /* just in case */
112 return NIL;
113 }
114 #endif
115 /* open connection */
116 if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
117 switch (errno) { /* analyze error */
118 case ECONNREFUSED:
119 s = "Refused";
120 break;
121 case ENOBUFS:
122 s = "Insufficient system resources";
123 break;
124 case ETIMEDOUT:
125 s = "Timed out";
126 break;
127 default:
128 s = "Unknown error";
129 break;
130 }
131 sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
132 mm_log (tmp,ERROR);
133 close (sock);
134 fs_give ((void **) &host);
135 return NIL;
136 }
137 /* create TCP/IP stream */
138 stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
139 stream->host = host; /* official host name */
140 stream->localhost = cpystr (mylocalhost ());
141 stream->port = port; /* port number */
142 stream->tcps = sock; /* init socket */
143 stream->ictr = 0; /* init input counter */
144 return stream; /* return success */
145 }
146
147 /* TCP/IP authenticated open
148 * Accepts: NETMBX specifier
149 * service name
150 * returned user name buffer
151 * Returns: TCP/IP stream if success else NIL
152 */
153
tcp_aopen(NETMBX * mb,char * service,char * usrbuf)154 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
155 {
156 return NIL; /* always NIL on DOS */
157 }
158
159 /* TCP receive line
160 * Accepts: TCP stream
161 * Returns: text line string or NIL if failure
162 */
163
tcp_getline(TCPSTREAM * stream)164 char *tcp_getline (TCPSTREAM *stream)
165 {
166 unsigned long n,contd;
167 char *ret = tcp_getline_work (stream,&n,&contd);
168 if (ret && contd) { /* got a line needing continuation? */
169 STRINGLIST *stl = mail_newstringlist ();
170 STRINGLIST *stc = stl;
171 do { /* collect additional lines */
172 stc->text.data = (unsigned char *) ret;
173 stc->text.size = n;
174 stc = stc->next = mail_newstringlist ();
175 ret = tcp_getline_work (stream,&n,&contd);
176 } while (ret && contd);
177 if (ret) { /* stash final part of line on list */
178 stc->text.data = (unsigned char *) ret;
179 stc->text.size = n;
180 /* determine how large a buffer we need */
181 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
182 ret = fs_get (n + 1); /* copy parts into buffer */
183 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
184 memcpy (ret + n,stc->text.data,stc->text.size);
185 ret[n] = '\0';
186 }
187 mail_free_stringlist (&stl);/* either way, done with list */
188 }
189 return ret;
190 }
191
192 /* TCP receive line or partial line
193 * Accepts: TCP stream
194 * pointer to return size
195 * pointer to return continuation flag
196 * Returns: text line string, size and continuation flag, or NIL if failure
197 */
198
tcp_getline_work(TCPSTREAM * stream,unsigned long * size,long * contd)199 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
200 long *contd)
201 {
202 unsigned long n;
203 char *s,*ret,c,d;
204 *contd = NIL; /* assume no continuation */
205 /* make sure have data */
206 if (!tcp_getdata (stream)) return NIL;
207 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
208 d = *stream->iptr++; /* slurp another character */
209 if ((c == '\015') && (d == '\012')) {
210 ret = (char *) fs_get (n--);
211 memcpy (ret,s,*size = n); /* copy into a free storage string */
212 ret[n] = '\0'; /* tie off string with null */
213 return ret;
214 }
215 }
216 /* copy partial string from buffer */
217 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
218 /* get more data from the net */
219 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
220 /* special case of newline broken by buffer */
221 else if ((c == '\015') && (*stream->iptr == '\012')) {
222 stream->iptr++; /* eat the line feed */
223 stream->ictr--;
224 ret[*size = --n] = '\0'; /* tie off string with null */
225 }
226 else *contd = LONGT; /* continuation needed */
227 return ret;
228 }
229
230 /* TCP/IP receive buffer
231 * Accepts: TCP/IP stream
232 * size in bytes
233 * buffer to read into
234 * Returns: T if success, NIL otherwise
235 */
236
tcp_getbuffer(TCPSTREAM * stream,unsigned long size,char * buffer)237 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
238 {
239 unsigned long n;
240 char *bufptr = buffer;
241 while (size > 0) { /* until request satisfied */
242 if (!tcp_getdata (stream)) return NIL;
243 n = min (size,stream->ictr);/* number of bytes to transfer */
244 /* do the copy */
245 memcpy (bufptr,stream->iptr,n);
246 bufptr += n; /* update pointer */
247 stream->iptr +=n;
248 size -= n; /* update # of bytes to do */
249 stream->ictr -=n;
250 }
251 bufptr[0] = '\0'; /* tie off string */
252 return T;
253 }
254
255 /* TCP/IP receive data
256 * Accepts: TCP/IP stream
257 * Returns: T if success, NIL otherwise
258 */
259
tcp_getdata(TCPSTREAM * stream)260 long tcp_getdata (TCPSTREAM *stream)
261 {
262 int i;
263 fd_set fds,efds;
264 struct timeval tmo;
265 time_t t = time (0);
266 if (stream->tcps < 0) return NIL;
267 while (stream->ictr < 1) { /* if nothing in the buffer */
268 time_t tl = time (0); /* start of request */
269 tmo.tv_sec = ttmo_read; /* read timeout */
270 tmo.tv_usec = 0;
271 FD_ZERO (&fds); /* initialize selection vector */
272 FD_ZERO (&efds); /* handle errors too */
273 FD_SET (stream->tcps,&fds);/* set bit in selection vector */
274 FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
275 errno = NIL; /* block and read */
276 while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
277 && (errno == EINTR));
278 if (!i) { /* timeout? */
279 time_t tc = time (0);
280 if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
281 else return tcp_abort (stream);
282 }
283 else if (i < 0) return tcp_abort (stream);
284 while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
285 (errno == EINTR));
286 if (i < 1) return tcp_abort (stream);
287 stream->iptr = stream->ibuf;/* point at TCP buffer */
288 stream->ictr = i; /* set new byte count */
289 }
290 return T;
291 }
292
293 /* TCP/IP send string as record
294 * Accepts: TCP/IP stream
295 * string pointer
296 * Returns: T if success else NIL
297 */
298
tcp_soutr(TCPSTREAM * stream,char * string)299 long tcp_soutr (TCPSTREAM *stream,char *string)
300 {
301 return tcp_sout (stream,string,(unsigned long) strlen (string));
302 }
303
304
305 /* TCP/IP send string
306 * Accepts: TCP/IP stream
307 * string pointer
308 * byte count
309 * Returns: T if success else NIL
310 */
311
tcp_sout(TCPSTREAM * stream,char * string,unsigned long size)312 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
313 {
314 int i;
315 fd_set fds;
316 struct timeval tmo;
317 time_t t = time (0);
318 if (stream->tcps < 0) return NIL;
319 while (size > 0) { /* until request satisfied */
320 time_t tl = time (0); /* start of request */
321 tmo.tv_sec = ttmo_write; /* write timeout */
322 tmo.tv_usec = 0;
323 FD_ZERO (&fds); /* initialize selection vector */
324 FD_SET (stream->tcps,&fds);/* set bit in selection vector */
325 errno = NIL; /* block and write */
326 while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
327 && (errno == EINTR));
328 if (!i) { /* timeout? */
329 time_t tc = time (0);
330 if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
331 else return tcp_abort (stream);
332 }
333 else if (i < 0) return tcp_abort (stream);
334 while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
335 if (i < 0) return tcp_abort (stream);
336 size -= i; /* how much we sent */
337 string += i;
338 }
339 return T; /* all done */
340 }
341
342 /* TCP/IP close
343 * Accepts: TCP/IP stream
344 */
345
tcp_close(TCPSTREAM * stream)346 void tcp_close (TCPSTREAM *stream)
347 {
348 tcp_abort (stream); /* nuke the socket */
349 /* flush host names */
350 fs_give ((void **) &stream->host);
351 fs_give ((void **) &stream->localhost);
352 fs_give ((void **) &stream); /* flush the stream */
353 }
354
355
356 /* TCP/IP abort stream
357 * Accepts: TCP/IP stream
358 * Returns: NIL always
359 */
360
tcp_abort(TCPSTREAM * stream)361 long tcp_abort (TCPSTREAM *stream)
362 {
363 if (stream->tcps >= 0) close (stream->tcps);
364 stream->tcps = -1;
365 return NIL;
366 }
367
368 /* TCP/IP get host name
369 * Accepts: TCP/IP stream
370 * Returns: host name for this stream
371 */
372
tcp_host(TCPSTREAM * stream)373 char *tcp_host (TCPSTREAM *stream)
374 {
375 return stream->host; /* return host name */
376 }
377
378
379 /* TCP/IP get remote host name
380 * Accepts: TCP/IP stream
381 * Returns: host name for this stream
382 */
383
tcp_remotehost(TCPSTREAM * stream)384 char *tcp_remotehost (TCPSTREAM *stream)
385 {
386 return stream->host; /* all we can do for now */
387 }
388
389
390 /* TCP/IP return port for this stream
391 * Accepts: TCP/IP stream
392 * Returns: port number for this stream
393 */
394
tcp_port(TCPSTREAM * stream)395 unsigned long tcp_port (TCPSTREAM *stream)
396 {
397 return stream->port; /* return port number */
398 }
399
400
401 /* TCP/IP get local host name
402 * Accepts: TCP/IP stream
403 * Returns: local host name
404 */
405
tcp_localhost(TCPSTREAM * stream)406 char *tcp_localhost (TCPSTREAM *stream)
407 {
408 return stream->localhost; /* return local host name */
409 }
410
411
412 /* TCP/IP return canonical form of host name
413 * Accepts: host name
414 * Returns: canonical form of host name
415 */
416
tcp_canonical(char * name)417 char *tcp_canonical (char *name)
418 {
419 return cpystr (name);
420 }
421
422
423 /* TCP/IP get client host name (server calls only)
424 * Returns: client host name
425 */
426
tcp_clienthost()427 char *tcp_clienthost ()
428 {
429 return "UNKNOWN";
430 }
431