1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
7 * Program: MS-DOS TCP/IP routines
12 * Last Edited: 3 April 2010
14 * Previous versions of this file were:
16 * Copyright 1988-2008 University of Washington
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
22 * http://www.apache.org/licenses/LICENSE-2.0
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;
29 static char *tcp_getline_work (TCPSTREAM
*stream
,unsigned long *size
,
32 /* TCP/IP manipulate parameters
33 * Accepts: function code
34 * function-dependent value
35 * Returns: function-dependent return value
38 void *tcp_parameters (long function
,void *value
)
41 switch ((int) function
) {
43 tmoh
= (tcptimeout_t
) value
;
48 ttmo_read
= (long) value
;
50 ret
= (void *) ttmo_read
;
52 case SET_WRITETIMEOUT
:
53 ttmo_write
= (long) value
;
54 case GET_WRITETIMEOUT
:
55 ret
= (void *) ttmo_write
;
63 * contact service name
65 * Returns: TCP/IP stream if success else NIL
68 TCPSTREAM
*tcp_open (char *host
,char *service
,unsigned long port
)
70 TCPSTREAM
*stream
= NIL
;
71 struct sockaddr_in sin
;
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
);
89 /* look up host name */
90 else if (!lookuphost (&host
,&sin
)) {
91 sprintf (tmp
,"Host not found: %s",host
);
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
);
102 fs_give ((void **) &host
);
107 else if (sock
>= FD_SETSIZE
) {/* unselectable sockets are useless */
108 sprintf (tmp
,"Unable to create selectable TCP socket (%d >= %d)",
111 errno
= ENOBUFS
; /* just in case */
115 /* open connection */
116 if (connect (sock
,(struct sockaddr
*) &sin
,sizeof (sin
)) < 0) {
117 switch (errno
) { /* analyze error */
122 s
= "Insufficient system resources";
131 sprintf (tmp
,"Can't connect to %.80s,%ld: %s (%d)",host
,port
,s
,errno
);
134 fs_give ((void **) &host
);
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 */
147 /* TCP/IP authenticated open
148 * Accepts: NETMBX specifier
150 * returned user name buffer
151 * Returns: TCP/IP stream if success else NIL
154 TCPSTREAM
*tcp_aopen (NETMBX
*mb
,char *service
,char *usrbuf
)
156 return NIL
; /* always NIL on DOS */
160 * Accepts: TCP stream
161 * Returns: text line string or NIL if failure
164 char *tcp_getline (TCPSTREAM
*stream
)
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
;
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
;
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
);
187 mail_free_stringlist (&stl
);/* either way, done with list */
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
199 static char *tcp_getline_work (TCPSTREAM
*stream
,unsigned long *size
,
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 */
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 */
224 ret
[*size
= --n
] = '\0'; /* tie off string with null */
226 else *contd
= LONGT
; /* continuation needed */
230 /* TCP/IP receive buffer
231 * Accepts: TCP/IP stream
233 * buffer to read into
234 * Returns: T if success, NIL otherwise
237 long tcp_getbuffer (TCPSTREAM
*stream
,unsigned long size
,char *buffer
)
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 */
245 memcpy (bufptr
,stream
->iptr
,n
);
246 bufptr
+= n
; /* update pointer */
248 size
-= n
; /* update # of bytes to do */
251 bufptr
[0] = '\0'; /* tie off string */
255 /* TCP/IP receive data
256 * Accepts: TCP/IP stream
257 * Returns: T if success, NIL otherwise
260 long tcp_getdata (TCPSTREAM
*stream
)
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 */
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
);
283 else if (i
< 0) return tcp_abort (stream
);
284 while (((i
= read (stream
->tcps
,stream
->ibuf
,BUFLEN
)) < 0) &&
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 */
293 /* TCP/IP send string as record
294 * Accepts: TCP/IP stream
296 * Returns: T if success else NIL
299 long tcp_soutr (TCPSTREAM
*stream
,char *string
)
301 return tcp_sout (stream
,string
,(unsigned long) strlen (string
));
305 /* TCP/IP send string
306 * Accepts: TCP/IP stream
309 * Returns: T if success else NIL
312 long tcp_sout (TCPSTREAM
*stream
,char *string
,unsigned long size
)
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 */
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
);
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 */
339 return T
; /* all done */
343 * Accepts: TCP/IP stream
346 void tcp_close (TCPSTREAM
*stream
)
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 */
356 /* TCP/IP abort stream
357 * Accepts: TCP/IP stream
358 * Returns: NIL always
361 long tcp_abort (TCPSTREAM
*stream
)
363 if (stream
->tcps
>= 0) close (stream
->tcps
);
368 /* TCP/IP get host name
369 * Accepts: TCP/IP stream
370 * Returns: host name for this stream
373 char *tcp_host (TCPSTREAM
*stream
)
375 return stream
->host
; /* return host name */
379 /* TCP/IP get remote host name
380 * Accepts: TCP/IP stream
381 * Returns: host name for this stream
384 char *tcp_remotehost (TCPSTREAM
*stream
)
386 return stream
->host
; /* all we can do for now */
390 /* TCP/IP return port for this stream
391 * Accepts: TCP/IP stream
392 * Returns: port number for this stream
395 unsigned long tcp_port (TCPSTREAM
*stream
)
397 return stream
->port
; /* return port number */
401 /* TCP/IP get local host name
402 * Accepts: TCP/IP stream
403 * Returns: local host name
406 char *tcp_localhost (TCPSTREAM
*stream
)
408 return stream
->localhost
; /* return local host name */
412 /* TCP/IP return canonical form of host name
414 * Returns: canonical form of host name
417 char *tcp_canonical (char *name
)
419 return cpystr (name
);
423 /* TCP/IP get client host name (server calls only)
424 * Returns: client host name
427 char *tcp_clienthost ()