imapext-2007

annotate src/osdep/amiga/tcp_ami.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
rev   line source
yuuji@0 1 /* ========================================================================
yuuji@0 2 * Copyright 1988-2008 University of Washington
yuuji@0 3 *
yuuji@0 4 * Licensed under the Apache License, Version 2.0 (the "License");
yuuji@0 5 * you may not use this file except in compliance with the License.
yuuji@0 6 * You may obtain a copy of the License at
yuuji@0 7 *
yuuji@0 8 * http://www.apache.org/licenses/LICENSE-2.0
yuuji@0 9 *
yuuji@0 10 *
yuuji@0 11 * ========================================================================
yuuji@0 12 */
yuuji@0 13
yuuji@0 14 /*
yuuji@0 15 * Program: Amiga TCP/IP routines
yuuji@0 16 *
yuuji@0 17 * Author: Mark Crispin
yuuji@0 18 * Networks and Distributed Computing
yuuji@0 19 * Computing & Communications
yuuji@0 20 * University of Washington
yuuji@0 21 * Administration Building, AG-44
yuuji@0 22 * Seattle, WA 98195
yuuji@0 23 * Internet: MRC@CAC.Washington.EDU
yuuji@0 24 *
yuuji@0 25 * Date: 1 August 1988
yuuji@0 26 * Last Edited: 13 January 2008
yuuji@0 27 */
yuuji@0 28
yuuji@0 29 #undef write /* don't use redefined write() */
yuuji@0 30
yuuji@0 31 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
yuuji@0 32 static long ttmo_open = 0; /* TCP timeouts, in seconds */
yuuji@0 33 static long ttmo_read = 0;
yuuji@0 34 static long ttmo_write = 0;
yuuji@0 35 static long allowreversedns = T;/* allow reverse DNS lookup */
yuuji@0 36 static long tcpdebug = NIL; /* extra TCP debugging telemetry */
yuuji@0 37
yuuji@0 38 extern long maxposint; /* get this from write.c */
yuuji@0 39
yuuji@0 40 /* Local function prototypes */
yuuji@0 41
yuuji@0 42 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
yuuji@0 43 unsigned long port);
yuuji@0 44 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
yuuji@0 45 long *contd);
yuuji@0 46 long tcp_abort (TCPSTREAM *stream);
yuuji@0 47 char *tcp_name (struct sockaddr_in *sin,long flag);
yuuji@0 48 char *tcp_name_valid (char *s);
yuuji@0 49
yuuji@0 50 /* TCP/IP manipulate parameters
yuuji@0 51 * Accepts: function code
yuuji@0 52 * function-dependent value
yuuji@0 53 * Returns: function-dependent return value
yuuji@0 54 */
yuuji@0 55
yuuji@0 56 void *tcp_parameters (long function,void *value)
yuuji@0 57 {
yuuji@0 58 void *ret = NIL;
yuuji@0 59 switch ((int) function) {
yuuji@0 60 case SET_TIMEOUT:
yuuji@0 61 tmoh = (tcptimeout_t) value;
yuuji@0 62 case GET_TIMEOUT:
yuuji@0 63 ret = (void *) tmoh;
yuuji@0 64 break;
yuuji@0 65 case SET_OPENTIMEOUT:
yuuji@0 66 ttmo_open = (long) value;
yuuji@0 67 case GET_OPENTIMEOUT:
yuuji@0 68 ret = (void *) ttmo_open;
yuuji@0 69 break;
yuuji@0 70 case SET_READTIMEOUT:
yuuji@0 71 ttmo_read = (long) value;
yuuji@0 72 case GET_READTIMEOUT:
yuuji@0 73 ret = (void *) ttmo_read;
yuuji@0 74 break;
yuuji@0 75 case SET_WRITETIMEOUT:
yuuji@0 76 ttmo_write = (long) value;
yuuji@0 77 case GET_WRITETIMEOUT:
yuuji@0 78 ret = (void *) ttmo_write;
yuuji@0 79 break;
yuuji@0 80 case SET_ALLOWREVERSEDNS:
yuuji@0 81 allowreversedns = (long) value;
yuuji@0 82 case GET_ALLOWREVERSEDNS:
yuuji@0 83 ret = (void *) allowreversedns;
yuuji@0 84 break;
yuuji@0 85 case SET_TCPDEBUG:
yuuji@0 86 tcpdebug = (long) value;
yuuji@0 87 case GET_TCPDEBUG:
yuuji@0 88 ret = (void *) tcpdebug;
yuuji@0 89 break;
yuuji@0 90 }
yuuji@0 91 return ret;
yuuji@0 92 }
yuuji@0 93
yuuji@0 94 /* TCP/IP open
yuuji@0 95 * Accepts: host name
yuuji@0 96 * contact service name
yuuji@0 97 * contact port number and optional silent flag
yuuji@0 98 * Returns: TCP/IP stream if success else NIL
yuuji@0 99 */
yuuji@0 100
yuuji@0 101 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
yuuji@0 102 {
yuuji@0 103 TCPSTREAM *stream = NIL;
yuuji@0 104 int i;
yuuji@0 105 int sock = -1;
yuuji@0 106 int ctr = 0;
yuuji@0 107 int silent = (port & NET_SILENT) ? T : NIL;
yuuji@0 108 int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
yuuji@0 109 char *s;
yuuji@0 110 struct sockaddr_in sin;
yuuji@0 111 struct hostent *he;
yuuji@0 112 char hostname[MAILTMPLEN];
yuuji@0 113 char tmp[MAILTMPLEN];
yuuji@0 114 struct servent *sv = NIL;
yuuji@0 115 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 116 void *data;
yuuji@0 117 port &= 0xffff; /* erase flags */
yuuji@0 118 /* lookup service */
yuuji@0 119 if (service && (sv = getservbyname (service,"tcp")))
yuuji@0 120 port = ntohs (sin.sin_port = sv->s_port);
yuuji@0 121 /* copy port number in network format */
yuuji@0 122 else sin.sin_port = htons (port);
yuuji@0 123 /* The domain literal form is used (rather than simply the dotted decimal
yuuji@0 124 as with other Amiga programs) because it has to be a valid "host name"
yuuji@0 125 in mailsystem terminology. */
yuuji@0 126 /* look like domain literal? */
yuuji@0 127 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
yuuji@0 128 strcpy (hostname,host+1); /* yes, copy number part */
yuuji@0 129 hostname[(strlen (hostname))-1] = '\0';
yuuji@0 130 if ((sin.sin_addr.s_addr = inet_addr (hostname)) == -1)
yuuji@0 131 sprintf (tmp,"Bad format domain-literal: %.80s",host);
yuuji@0 132 else {
yuuji@0 133 sin.sin_family = AF_INET; /* family is always Internet */
yuuji@0 134 strcpy (hostname,host); /* hostname is user's argument */
yuuji@0 135 (*bn) (BLOCK_TCPOPEN,NIL);
yuuji@0 136 /* get an open socket for this system */
yuuji@0 137 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
yuuji@0 138 (*bn) (BLOCK_NONE,NIL);
yuuji@0 139 }
yuuji@0 140 }
yuuji@0 141
yuuji@0 142 else { /* lookup host name */
yuuji@0 143 if (tcpdebug) {
yuuji@0 144 sprintf (tmp,"DNS resolution %.80s",host);
yuuji@0 145 mm_log (tmp,TCPDEBUG);
yuuji@0 146 }
yuuji@0 147 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
yuuji@0 148 data = (*bn) (BLOCK_SENSITIVE,NIL);
yuuji@0 149 if (!(he = gethostbyname (lcase (strcpy (hostname,host)))))
yuuji@0 150 sprintf (tmp,"No such host as %.80s",host);
yuuji@0 151 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 152 (*bn) (BLOCK_NONE,NIL);
yuuji@0 153 if (he) { /* DNS resolution won? */
yuuji@0 154 if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
yuuji@0 155 /* copy address type */
yuuji@0 156 sin.sin_family = he->h_addrtype;
yuuji@0 157 /* copy host name */
yuuji@0 158 strcpy (hostname,he->h_name);
yuuji@0 159 #ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */
yuuji@0 160 for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) {
yuuji@0 161 if (i && !silent) mm_log (tmp,WARN);
yuuji@0 162 memcpy (&sin.sin_addr,s,he->h_length);
yuuji@0 163 (*bn) (BLOCK_TCPOPEN,NIL);
yuuji@0 164 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
yuuji@0 165 (*bn) (BLOCK_NONE,NIL);
yuuji@0 166 }
yuuji@0 167 #else /* the one true address then */
yuuji@0 168 memcpy (&sin.sin_addr,he->h_addr,he->h_length);
yuuji@0 169 (*bn) (BLOCK_TCPOPEN,NIL);
yuuji@0 170 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
yuuji@0 171 (*bn) (BLOCK_NONE,NIL);
yuuji@0 172 #endif
yuuji@0 173 }
yuuji@0 174 }
yuuji@0 175 if (sock >= 0) { /* won */
yuuji@0 176 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
yuuji@0 177 sizeof (TCPSTREAM));
yuuji@0 178 stream->port = port; /* port number */
yuuji@0 179 /* init sockets */
yuuji@0 180 stream->tcpsi = stream->tcpso = sock;
yuuji@0 181 /* stash in the snuck-in byte */
yuuji@0 182 if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
yuuji@0 183 /* copy official host name */
yuuji@0 184 stream->host = cpystr (hostname);
yuuji@0 185 if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
yuuji@0 186 }
yuuji@0 187 else if (!silent) mm_log (tmp,ERROR);
yuuji@0 188 return stream; /* return success */
yuuji@0 189 }
yuuji@0 190
yuuji@0 191 /* Open a TCP socket
yuuji@0 192 * Accepts: Internet socket address block
yuuji@0 193 * scratch buffer
yuuji@0 194 * pointer to "first byte read in" storage or NIL
yuuji@0 195 * host name for error message
yuuji@0 196 * port number for error message
yuuji@0 197 * Returns: socket if success, else -1 with error string in scratch buffer
yuuji@0 198 */
yuuji@0 199
yuuji@0 200 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
yuuji@0 201 unsigned long port)
yuuji@0 202 {
yuuji@0 203 int i,ti,sock,flgs;
yuuji@0 204 time_t now;
yuuji@0 205 struct protoent *pt = getprotobyname ("tcp");
yuuji@0 206 fd_set fds,efds;
yuuji@0 207 struct timeval tmo;
yuuji@0 208 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 209 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
yuuji@0 210 sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
yuuji@0 211 mm_log (tmp,NIL);
yuuji@0 212 /* make a socket */
yuuji@0 213 if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
yuuji@0 214 sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
yuuji@0 215 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 216 return -1;
yuuji@0 217 }
yuuji@0 218 else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
yuuji@0 219 sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
yuuji@0 220 sock,FD_SETSIZE);
yuuji@0 221 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 222 close (sock);
yuuji@0 223 errno = EMFILE;
yuuji@0 224 return -1;
yuuji@0 225 }
yuuji@0 226 flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
yuuji@0 227 /* set non-blocking if want open timeout */
yuuji@0 228 if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
yuuji@0 229 /* open connection */
yuuji@0 230 while ((i = connect (sock,(struct sockaddr *) sin,
yuuji@0 231 sizeof (struct sockaddr_in))) < 0 && (errno == EINTR));
yuuji@0 232 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 233 if (i < 0) switch (errno) { /* failed? */
yuuji@0 234 case EAGAIN: /* DG brain damage */
yuuji@0 235 case EINPROGRESS: /* what we expect to happen */
yuuji@0 236 case EALREADY: /* or another form of it */
yuuji@0 237 case EISCONN: /* restart after interrupt? */
yuuji@0 238 case EADDRINUSE: /* restart after interrupt? */
yuuji@0 239 break; /* well, not really, it was interrupted */
yuuji@0 240 default:
yuuji@0 241 sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
yuuji@0 242 close (sock); /* flush socket */
yuuji@0 243 return -1;
yuuji@0 244 }
yuuji@0 245
yuuji@0 246 if (ctr) { /* want open timeout */
yuuji@0 247 now = time (0); /* open timeout */
yuuji@0 248 ti = ttmo_open ? now + ttmo_open : 0;
yuuji@0 249 tmo.tv_usec = 0;
yuuji@0 250 FD_ZERO (&fds); /* initialize selection vector */
yuuji@0 251 FD_ZERO (&efds); /* handle errors too */
yuuji@0 252 FD_SET (sock,&fds); /* block for error or readable */
yuuji@0 253 FD_SET (sock,&efds);
yuuji@0 254 do { /* block under timeout */
yuuji@0 255 tmo.tv_sec = ti ? ti - now : 0;
yuuji@0 256 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
yuuji@0 257 now = time (0); /* fake timeout if interrupt & time expired */
yuuji@0 258 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
yuuji@0 259 } while ((i < 0) && (errno == EINTR));
yuuji@0 260 if (i > 0) { /* success, make sure really connected */
yuuji@0 261 fcntl (sock,F_SETFL,flgs);/* restore blocking status */
yuuji@0 262 /* This used to be a zero-byte read(), but that crashes Solaris */
yuuji@0 263 /* get socket status */
yuuji@0 264 while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
yuuji@0 265 }
yuuji@0 266 if (i <= 0) { /* timeout or error? */
yuuji@0 267 i = i ? errno : ETIMEDOUT;/* determine error code */
yuuji@0 268 close (sock); /* flush socket */
yuuji@0 269 errno = i; /* return error code */
yuuji@0 270 sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port,
yuuji@0 271 strerror (errno));
yuuji@0 272 return -1;
yuuji@0 273 }
yuuji@0 274 }
yuuji@0 275 return sock; /* return the socket */
yuuji@0 276 }
yuuji@0 277
yuuji@0 278 /* TCP/IP authenticated open
yuuji@0 279 * Accepts: host name
yuuji@0 280 * service name
yuuji@0 281 * returned user name buffer
yuuji@0 282 * Returns: TCP/IP stream if success else NIL
yuuji@0 283 */
yuuji@0 284
yuuji@0 285 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
yuuji@0 286 {
yuuji@0 287 return NIL; /* disabled */
yuuji@0 288 }
yuuji@0 289
yuuji@0 290 /* TCP receive line
yuuji@0 291 * Accepts: TCP stream
yuuji@0 292 * Returns: text line string or NIL if failure
yuuji@0 293 */
yuuji@0 294
yuuji@0 295 char *tcp_getline (TCPSTREAM *stream)
yuuji@0 296 {
yuuji@0 297 unsigned long n,contd;
yuuji@0 298 char *ret = tcp_getline_work (stream,&n,&contd);
yuuji@0 299 if (ret && contd) { /* got a line needing continuation? */
yuuji@0 300 STRINGLIST *stl = mail_newstringlist ();
yuuji@0 301 STRINGLIST *stc = stl;
yuuji@0 302 do { /* collect additional lines */
yuuji@0 303 stc->text.data = (unsigned char *) ret;
yuuji@0 304 stc->text.size = n;
yuuji@0 305 stc = stc->next = mail_newstringlist ();
yuuji@0 306 ret = tcp_getline_work (stream,&n,&contd);
yuuji@0 307 } while (ret && contd);
yuuji@0 308 if (ret) { /* stash final part of line on list */
yuuji@0 309 stc->text.data = (unsigned char *) ret;
yuuji@0 310 stc->text.size = n;
yuuji@0 311 /* determine how large a buffer we need */
yuuji@0 312 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
yuuji@0 313 ret = fs_get (n + 1); /* copy parts into buffer */
yuuji@0 314 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
yuuji@0 315 memcpy (ret + n,stc->text.data,stc->text.size);
yuuji@0 316 ret[n] = '\0';
yuuji@0 317 }
yuuji@0 318 mail_free_stringlist (&stl);/* either way, done with list */
yuuji@0 319 }
yuuji@0 320 return ret;
yuuji@0 321 }
yuuji@0 322
yuuji@0 323 /* TCP receive line or partial line
yuuji@0 324 * Accepts: TCP stream
yuuji@0 325 * pointer to return size
yuuji@0 326 * pointer to return continuation flag
yuuji@0 327 * Returns: text line string, size and continuation flag, or NIL if failure
yuuji@0 328 */
yuuji@0 329
yuuji@0 330 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
yuuji@0 331 long *contd)
yuuji@0 332 {
yuuji@0 333 unsigned long n;
yuuji@0 334 char *s,*ret,c,d;
yuuji@0 335 *contd = NIL; /* assume no continuation */
yuuji@0 336 /* make sure have data */
yuuji@0 337 if (!tcp_getdata (stream)) return NIL;
yuuji@0 338 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
yuuji@0 339 d = *stream->iptr++; /* slurp another character */
yuuji@0 340 if ((c == '\015') && (d == '\012')) {
yuuji@0 341 ret = (char *) fs_get (n--);
yuuji@0 342 memcpy (ret,s,*size = n); /* copy into a free storage string */
yuuji@0 343 ret[n] = '\0'; /* tie off string with null */
yuuji@0 344 return ret;
yuuji@0 345 }
yuuji@0 346 }
yuuji@0 347 /* copy partial string from buffer */
yuuji@0 348 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
yuuji@0 349 /* get more data from the net */
yuuji@0 350 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
yuuji@0 351 /* special case of newline broken by buffer */
yuuji@0 352 else if ((c == '\015') && (*stream->iptr == '\012')) {
yuuji@0 353 stream->iptr++; /* eat the line feed */
yuuji@0 354 stream->ictr--;
yuuji@0 355 ret[*size = --n] = '\0'; /* tie off string with null */
yuuji@0 356 }
yuuji@0 357 else *contd = LONGT; /* continuation needed */
yuuji@0 358 return ret;
yuuji@0 359 }
yuuji@0 360
yuuji@0 361 /* TCP/IP receive buffer
yuuji@0 362 * Accepts: TCP/IP stream
yuuji@0 363 * size in bytes
yuuji@0 364 * buffer to read into
yuuji@0 365 * Returns: T if success, NIL otherwise
yuuji@0 366 */
yuuji@0 367
yuuji@0 368 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
yuuji@0 369 {
yuuji@0 370 unsigned long n;
yuuji@0 371 /* make sure socket still alive */
yuuji@0 372 if (stream->tcpsi < 0) return NIL;
yuuji@0 373 /* can transfer bytes from buffer? */
yuuji@0 374 if (n = min (size,stream->ictr)) {
yuuji@0 375 memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
yuuji@0 376 s += n; /* update pointer */
yuuji@0 377 stream->iptr +=n;
yuuji@0 378 size -= n; /* update # of bytes to do */
yuuji@0 379 stream->ictr -=n;
yuuji@0 380 }
yuuji@0 381 if (size) {
yuuji@0 382 int i;
yuuji@0 383 fd_set fds,efds;
yuuji@0 384 struct timeval tmo;
yuuji@0 385 time_t t = time (0);
yuuji@0 386 blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 387 (*bn) (BLOCK_TCPREAD,NIL);
yuuji@0 388 while (size > 0) { /* until request satisfied */
yuuji@0 389 time_t tl = time (0);
yuuji@0 390 time_t now = tl;
yuuji@0 391 time_t ti = ttmo_read ? now + ttmo_read : 0;
yuuji@0 392 if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
yuuji@0 393 tmo.tv_usec = 0;
yuuji@0 394 FD_ZERO (&fds); /* initialize selection vector */
yuuji@0 395 FD_ZERO (&efds); /* handle errors too */
yuuji@0 396 FD_SET (stream->tcpsi,&fds);
yuuji@0 397 FD_SET (stream->tcpsi,&efds);
yuuji@0 398 errno = NIL; /* block and read */
yuuji@0 399 do { /* block under timeout */
yuuji@0 400 tmo.tv_sec = ti ? ti - now : 0;
yuuji@0 401 i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
yuuji@0 402 now = time (0); /* fake timeout if interrupt & time expired */
yuuji@0 403 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
yuuji@0 404 } while ((i < 0) && (errno == EINTR));
yuuji@0 405 if (i > 0) { /* select says there's data to read? */
yuuji@0 406 while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
yuuji@0 407 (errno == EINTR));
yuuji@0 408 if (i < 1) return tcp_abort (stream);
yuuji@0 409 s += i; /* point at new place to write */
yuuji@0 410 size -= i; /* reduce byte count */
yuuji@0 411 if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
yuuji@0 412 }
yuuji@0 413 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
yuuji@0 414 return tcp_abort (stream);
yuuji@0 415 }
yuuji@0 416 (*bn) (BLOCK_NONE,NIL);
yuuji@0 417 }
yuuji@0 418 *s = '\0'; /* tie off string */
yuuji@0 419 return T;
yuuji@0 420 }
yuuji@0 421
yuuji@0 422 /* TCP/IP receive data
yuuji@0 423 * Accepts: TCP/IP stream
yuuji@0 424 * Returns: T if success, NIL otherwise
yuuji@0 425 */
yuuji@0 426
yuuji@0 427 long tcp_getdata (TCPSTREAM *stream)
yuuji@0 428 {
yuuji@0 429 int i;
yuuji@0 430 fd_set fds,efds;
yuuji@0 431 struct timeval tmo;
yuuji@0 432 time_t t = time (0);
yuuji@0 433 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 434 if (stream->tcpsi < 0) return NIL;
yuuji@0 435 (*bn) (BLOCK_TCPREAD,NIL);
yuuji@0 436 while (stream->ictr < 1) { /* if nothing in the buffer */
yuuji@0 437 time_t tl = time (0); /* start of request */
yuuji@0 438 time_t now = tl;
yuuji@0 439 time_t ti = ttmo_read ? now + ttmo_read : 0;
yuuji@0 440 if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
yuuji@0 441 tmo.tv_usec = 0;
yuuji@0 442 FD_ZERO (&fds); /* initialize selection vector */
yuuji@0 443 FD_ZERO (&efds); /* handle errors too */
yuuji@0 444 FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
yuuji@0 445 FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
yuuji@0 446 errno = NIL; /* block and read */
yuuji@0 447 do { /* block under timeout */
yuuji@0 448 tmo.tv_sec = ti ? ti - now : 0;
yuuji@0 449 i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
yuuji@0 450 now = time (0); /* fake timeout if interrupt & time expired */
yuuji@0 451 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
yuuji@0 452 } while ((i < 0) && (errno == EINTR));
yuuji@0 453 if (i > 0) { /* got data? */
yuuji@0 454 while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
yuuji@0 455 (errno == EINTR));
yuuji@0 456 if (i < 1) return tcp_abort (stream);
yuuji@0 457 stream->iptr = stream->ibuf;/* point at TCP buffer */
yuuji@0 458 stream->ictr = i; /* set new byte count */
yuuji@0 459 if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
yuuji@0 460 }
yuuji@0 461 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
yuuji@0 462 return tcp_abort (stream);/* error or timeout no-continue */
yuuji@0 463 }
yuuji@0 464 (*bn) (BLOCK_NONE,NIL);
yuuji@0 465 return T;
yuuji@0 466 }
yuuji@0 467
yuuji@0 468 /* TCP/IP send string as record
yuuji@0 469 * Accepts: TCP/IP stream
yuuji@0 470 * string pointer
yuuji@0 471 * Returns: T if success else NIL
yuuji@0 472 */
yuuji@0 473
yuuji@0 474 long tcp_soutr (TCPSTREAM *stream,char *string)
yuuji@0 475 {
yuuji@0 476 return tcp_sout (stream,string,(unsigned long) strlen (string));
yuuji@0 477 }
yuuji@0 478
yuuji@0 479
yuuji@0 480 /* TCP/IP send string
yuuji@0 481 * Accepts: TCP/IP stream
yuuji@0 482 * string pointer
yuuji@0 483 * byte count
yuuji@0 484 * Returns: T if success else NIL
yuuji@0 485 */
yuuji@0 486
yuuji@0 487 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
yuuji@0 488 {
yuuji@0 489 int i;
yuuji@0 490 fd_set fds,efds;
yuuji@0 491 struct timeval tmo;
yuuji@0 492 time_t t = time (0);
yuuji@0 493 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 494 if (stream->tcpso < 0) return NIL;
yuuji@0 495 (*bn) (BLOCK_TCPWRITE,NIL);
yuuji@0 496 while (size > 0) { /* until request satisfied */
yuuji@0 497 time_t tl = time (0); /* start of request */
yuuji@0 498 time_t now = tl;
yuuji@0 499 time_t ti = ttmo_write ? now + ttmo_write : 0;
yuuji@0 500 if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
yuuji@0 501 tmo.tv_usec = 0;
yuuji@0 502 FD_ZERO (&fds); /* initialize selection vector */
yuuji@0 503 FD_ZERO (&efds); /* handle errors too */
yuuji@0 504 FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
yuuji@0 505 FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
yuuji@0 506 errno = NIL; /* block and write */
yuuji@0 507 do { /* block under timeout */
yuuji@0 508 tmo.tv_sec = ti ? ti - now : 0;
yuuji@0 509 i = select (stream->tcpso+1,0,&fds,&efds,ti ? &tmo : 0);
yuuji@0 510 now = time (0); /* fake timeout if interrupt & time expired */
yuuji@0 511 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
yuuji@0 512 } while ((i < 0) && (errno == EINTR));
yuuji@0 513 if (i > 0) { /* OK to send data? */
yuuji@0 514 while (((i = write (stream->tcpso,string,size)) < 0) &&(errno == EINTR));
yuuji@0 515 if (i < 0) return tcp_abort (stream);
yuuji@0 516 size -= i; /* how much we sent */
yuuji@0 517 string += i;
yuuji@0 518 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
yuuji@0 519 }
yuuji@0 520 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
yuuji@0 521 return tcp_abort (stream);/* error or timeout no-continue */
yuuji@0 522 }
yuuji@0 523 (*bn) (BLOCK_NONE,NIL);
yuuji@0 524 return T; /* all done */
yuuji@0 525 }
yuuji@0 526
yuuji@0 527 /* TCP/IP close
yuuji@0 528 * Accepts: TCP/IP stream
yuuji@0 529 */
yuuji@0 530
yuuji@0 531 void tcp_close (TCPSTREAM *stream)
yuuji@0 532 {
yuuji@0 533 tcp_abort (stream); /* nuke the stream */
yuuji@0 534 /* flush host names */
yuuji@0 535 if (stream->host) fs_give ((void **) &stream->host);
yuuji@0 536 if (stream->remotehost) fs_give ((void **) &stream->remotehost);
yuuji@0 537 if (stream->localhost) fs_give ((void **) &stream->localhost);
yuuji@0 538 fs_give ((void **) &stream); /* flush the stream */
yuuji@0 539 }
yuuji@0 540
yuuji@0 541
yuuji@0 542 /* TCP/IP abort stream
yuuji@0 543 * Accepts: TCP/IP stream
yuuji@0 544 * Returns: NIL always
yuuji@0 545 */
yuuji@0 546
yuuji@0 547 long tcp_abort (TCPSTREAM *stream)
yuuji@0 548 {
yuuji@0 549 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 550 if (stream->tcpsi >= 0) { /* no-op if no socket */
yuuji@0 551 (*bn) (BLOCK_TCPCLOSE,NIL);
yuuji@0 552 close (stream->tcpsi); /* nuke the socket */
yuuji@0 553 if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
yuuji@0 554 stream->tcpsi = stream->tcpso = -1;
yuuji@0 555 }
yuuji@0 556 (*bn) (BLOCK_NONE,NIL);
yuuji@0 557 return NIL;
yuuji@0 558 }
yuuji@0 559
yuuji@0 560 /* TCP/IP get host name
yuuji@0 561 * Accepts: TCP/IP stream
yuuji@0 562 * Returns: host name for this stream
yuuji@0 563 */
yuuji@0 564
yuuji@0 565 char *tcp_host (TCPSTREAM *stream)
yuuji@0 566 {
yuuji@0 567 return stream->host; /* use tcp_remotehost() if want guarantees */
yuuji@0 568 }
yuuji@0 569
yuuji@0 570
yuuji@0 571 /* TCP/IP get remote host name
yuuji@0 572 * Accepts: TCP/IP stream
yuuji@0 573 * Returns: host name for this stream
yuuji@0 574 */
yuuji@0 575
yuuji@0 576 char *tcp_remotehost (TCPSTREAM *stream)
yuuji@0 577 {
yuuji@0 578 if (!stream->remotehost) {
yuuji@0 579 struct sockaddr_in sin;
yuuji@0 580 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 581 stream->remotehost = /* get socket's peer name */
yuuji@0 582 (getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
yuuji@0 583 (sin.sin_family != AF_INET)) ?
yuuji@0 584 cpystr (stream->host) : tcp_name (&sin,NIL);
yuuji@0 585 }
yuuji@0 586 return stream->remotehost;
yuuji@0 587 }
yuuji@0 588
yuuji@0 589
yuuji@0 590 /* TCP/IP return port for this stream
yuuji@0 591 * Accepts: TCP/IP stream
yuuji@0 592 * Returns: port number for this stream
yuuji@0 593 */
yuuji@0 594
yuuji@0 595 unsigned long tcp_port (TCPSTREAM *stream)
yuuji@0 596 {
yuuji@0 597 return stream->port; /* return port number */
yuuji@0 598 }
yuuji@0 599
yuuji@0 600
yuuji@0 601 /* TCP/IP get local host name
yuuji@0 602 * Accepts: TCP/IP stream
yuuji@0 603 * Returns: local host name
yuuji@0 604 */
yuuji@0 605
yuuji@0 606 char *tcp_localhost (TCPSTREAM *stream)
yuuji@0 607 {
yuuji@0 608 if (!stream->localhost) {
yuuji@0 609 struct sockaddr_in sin;
yuuji@0 610 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 611 stream->localhost = /* get socket's name */
yuuji@0 612 ((stream->port & 0xffff000) ||
yuuji@0 613 getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
yuuji@0 614 (sin.sin_family != AF_INET)) ?
yuuji@0 615 cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
yuuji@0 616 }
yuuji@0 617 return stream->localhost; /* return local host name */
yuuji@0 618 }
yuuji@0 619
yuuji@0 620 /* TCP/IP get client host address (server calls only)
yuuji@0 621 * Returns: client host address
yuuji@0 622 */
yuuji@0 623
yuuji@0 624 static char *myClientAddr = NIL;
yuuji@0 625
yuuji@0 626 char *tcp_clientaddr ()
yuuji@0 627 {
yuuji@0 628 if (!myClientAddr) {
yuuji@0 629 struct sockaddr_in sin;
yuuji@0 630 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 631 myClientAddr = /* get stdin's peer name */
yuuji@0 632 cpystr (getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
yuuji@0 633 "UNKNOWN" : ((sin.sin_family == AF_INET) ?
yuuji@0 634 inet_ntoa (sin.sin_addr) : "NON-IPv4"));
yuuji@0 635 }
yuuji@0 636 return myClientAddr;
yuuji@0 637 }
yuuji@0 638
yuuji@0 639
yuuji@0 640 /* TCP/IP get client host name (server calls only)
yuuji@0 641 * Returns: client host name
yuuji@0 642 */
yuuji@0 643
yuuji@0 644 static char *myClientHost = NIL;
yuuji@0 645
yuuji@0 646 char *tcp_clienthost ()
yuuji@0 647 {
yuuji@0 648 if (!myClientHost) {
yuuji@0 649 struct sockaddr_in sin;
yuuji@0 650 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 651 myClientHost = /* get stdin's peer name */
yuuji@0 652 getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
yuuji@0 653 cpystr ("UNKNOWN") : ((sin.sin_family == AF_INET) ?
yuuji@0 654 tcp_name (&sin,T) : cpystr ("NON-IPv4"));
yuuji@0 655 }
yuuji@0 656 return myClientHost;
yuuji@0 657 }
yuuji@0 658
yuuji@0 659 /* TCP/IP get server host address (server calls only)
yuuji@0 660 * Returns: server host address
yuuji@0 661 */
yuuji@0 662
yuuji@0 663 static char *myServerAddr = NIL;
yuuji@0 664
yuuji@0 665 char *tcp_serveraddr ()
yuuji@0 666 {
yuuji@0 667 if (!myServerAddr) {
yuuji@0 668 struct sockaddr_in sin;
yuuji@0 669 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 670 myServerAddr = /* get stdin's peer name */
yuuji@0 671 cpystr (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
yuuji@0 672 "UNKNOWN" : ((sin.sin_family == AF_INET) ?
yuuji@0 673 inet_ntoa (sin.sin_addr) : "NON-IPv4"));
yuuji@0 674 }
yuuji@0 675 return myServerAddr;
yuuji@0 676 }
yuuji@0 677
yuuji@0 678
yuuji@0 679 /* TCP/IP get server host name (server calls only)
yuuji@0 680 * Returns: server host name
yuuji@0 681 */
yuuji@0 682
yuuji@0 683 static char *myServerHost = NIL;
yuuji@0 684 static long myServerPort = -1;
yuuji@0 685
yuuji@0 686 char *tcp_serverhost ()
yuuji@0 687 {
yuuji@0 688 if (!myServerHost) {
yuuji@0 689 struct sockaddr_in sin;
yuuji@0 690 int sinlen = sizeof (struct sockaddr_in);
yuuji@0 691 /* get stdin's name */
yuuji@0 692 if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ||
yuuji@0 693 (sin.sin_family != AF_INET)) myServerHost = cpystr (mylocalhost ());
yuuji@0 694 else {
yuuji@0 695 myServerHost = tcp_name (&sin,NIL);
yuuji@0 696 myServerPort = ntohs (sin.sin_port);
yuuji@0 697 }
yuuji@0 698 }
yuuji@0 699 return myServerHost;
yuuji@0 700 }
yuuji@0 701
yuuji@0 702
yuuji@0 703 /* TCP/IP get server port number (server calls only)
yuuji@0 704 * Returns: server port number
yuuji@0 705 */
yuuji@0 706
yuuji@0 707 long tcp_serverport ()
yuuji@0 708 {
yuuji@0 709 if (!myServerHost) tcp_serverhost ();
yuuji@0 710 return myServerPort;
yuuji@0 711 }
yuuji@0 712
yuuji@0 713 /* TCP/IP return canonical form of host name
yuuji@0 714 * Accepts: host name
yuuji@0 715 * Returns: canonical form of host name
yuuji@0 716 */
yuuji@0 717
yuuji@0 718 char *tcp_canonical (char *name)
yuuji@0 719 {
yuuji@0 720 char *ret,host[MAILTMPLEN];
yuuji@0 721 struct hostent *he;
yuuji@0 722 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 723 void *data;
yuuji@0 724 /* look like domain literal? */
yuuji@0 725 if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
yuuji@0 726 (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
yuuji@0 727 data = (*bn) (BLOCK_SENSITIVE,NIL);
yuuji@0 728 if (tcpdebug) {
yuuji@0 729 sprintf (host,"DNS canonicalization %.80s",name);
yuuji@0 730 mm_log (host,TCPDEBUG);
yuuji@0 731 }
yuuji@0 732 /* note that Amiga requires lowercase! */
yuuji@0 733 ret = (he = gethostbyname (lcase (strcpy (host,name)))) ?
yuuji@0 734 (char *) he->h_name : name;
yuuji@0 735 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 736 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
yuuji@0 737 if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
yuuji@0 738 return ret;
yuuji@0 739 }
yuuji@0 740
yuuji@0 741
yuuji@0 742 /* TCP/IP return name from socket
yuuji@0 743 * Accepts: socket
yuuji@0 744 * verbose flag
yuuji@0 745 * Returns: cpystr name
yuuji@0 746 */
yuuji@0 747
yuuji@0 748 char *tcp_name (struct sockaddr_in *sin,long flag)
yuuji@0 749 {
yuuji@0 750 char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
yuuji@0 751 sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
yuuji@0 752 if (allowreversedns) {
yuuji@0 753 struct hostent *he;
yuuji@0 754 blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 755 void *data;
yuuji@0 756 if (tcpdebug) {
yuuji@0 757 sprintf (tmp,"Reverse DNS resolution %s",adr);
yuuji@0 758 mm_log (tmp,TCPDEBUG);
yuuji@0 759 }
yuuji@0 760 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
yuuji@0 761 data = (*bn) (BLOCK_SENSITIVE,NIL);
yuuji@0 762 /* translate address to name */
yuuji@0 763 if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
yuuji@0 764 sizeof (struct in_addr),
yuuji@0 765 sin->sin_family)) ?
yuuji@0 766 (char *) he->h_name : NIL)) {
yuuji@0 767 /* produce verbose form if needed */
yuuji@0 768 if (flag) sprintf (ret = tmp,"%s %s",t,adr);
yuuji@0 769 else ret = t;
yuuji@0 770 }
yuuji@0 771 (*bn) (BLOCK_NONSENSITIVE,data);
yuuji@0 772 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
yuuji@0 773 if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
yuuji@0 774 }
yuuji@0 775 return cpystr (ret);
yuuji@0 776 }
yuuji@0 777
yuuji@0 778
yuuji@0 779 /* Validate name
yuuji@0 780 * Accepts: domain name
yuuji@0 781 * Returns: T if valid, NIL otherwise
yuuji@0 782 */
yuuji@0 783
yuuji@0 784 char *tcp_name_valid (char *s)
yuuji@0 785 {
yuuji@0 786 int c;
yuuji@0 787 char *ret,*tail;
yuuji@0 788 /* must be non-empty and not too long */
yuuji@0 789 if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
yuuji@0 790 /* must be alnum, dot, or hyphen */
yuuji@0 791 while ((c = *s++) && (s <= tail) &&
yuuji@0 792 (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
yuuji@0 793 ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
yuuji@0 794 if (c) ret = NIL;
yuuji@0 795 }
yuuji@0 796 return ret;
yuuji@0 797 }

UW-IMAP'd extensions by yuuji