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