/* ======================================================================== * Copyright 2008-2010 Mark Crispin * ======================================================================== */ /* * Program: VMS TCP/IP routines for Multinet * * Author: Mark Crispin * * Date: 2 August 1994 * Last Edited: 3 April 2010 * * Previous versions of this file were: * * Copyright 1988-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */ static long ttmo_read = 0; /* TCP timeouts, in seconds */ static long ttmo_write = 0; static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size, long *contd); /* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */ void *tcp_parameters (long function,void *value) { void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; } return ret; } /* TCP/IP open * Accepts: host name * contact service name * contact port number * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port) { TCPSTREAM *stream = NIL; int sock; char *s; struct sockaddr_in sin; struct hostent *host_name; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct protoent *pt = getprotobyname ("tcp"); struct servent *sv = NIL; port &= 0xffff; /* erase flags */ if (service) { /* service specified? */ if (*service == '*') { /* yes, special alt driver kludge? */ sv = getservbyname (service + 1,"tcp"); } else sv = getservbyname (service,"tcp"); } /* user service name port */ if (sv) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons (port); /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (hostname,host+1); /* yes, copy number part */ hostname[(strlen (hostname))-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); /* hostname is user's argument */ } else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* lookup host name, note that brain-dead Unix requires lowercase! */ strcpy (hostname,host); /* in case host is in write-protected memory */ if ((host_name = gethostbyname (lcase (hostname)))) { /* copy address type */ sin.sin_family = host_name->h_addrtype; /* copy host name */ strcpy (hostname,host_name->h_name); /* copy host addresses */ memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length); } else { sprintf (tmp,"No such host as %.80s",host); mm_log (tmp,ERROR); return NIL; } } /* get a TCP stream */ if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) { sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } #if 0 /* Maybe this test is necessary. It depends upon how VMS implements * fd_set. UNIX-style fd_set needs it; Windows-style does not. */ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)", sock,FD_SETSIZE); close (sock); return NIL; } #endif /* open connection */ if (connect (sock,(struct sockaddr *)&sin,sizeof (sin)) < 0) { sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port, strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* create TCP/IP stream */ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM)); /* copy official host name */ stream->host = cpystr (hostname); /* get local name */ gethostname (tmp,MAILTMPLEN-1); stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ? host_name->h_name : tmp); /* init sockets */ stream->port = port; /* port number */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ return stream; /* return success */ } /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf) { return NIL; } /* TCP receive line * Accepts: TCP stream * Returns: text line string or NIL if failure */ char *tcp_getline (TCPSTREAM *stream) { unsigned long n,contd; char *ret = tcp_getline_work (stream,&n,&contd); if (ret && contd) { /* got a line needing continuation? */ STRINGLIST *stl = mail_newstringlist (); STRINGLIST *stc = stl; do { /* collect additional lines */ stc->text.data = (unsigned char *) ret; stc->text.size = n; stc = stc->next = mail_newstringlist (); ret = tcp_getline_work (stream,&n,&contd); } while (ret && contd); if (ret) { /* stash final part of line on list */ stc->text.data = (unsigned char *) ret; stc->text.size = n; /* determine how large a buffer we need */ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size; ret = fs_get (n + 1); /* copy parts into buffer */ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next) memcpy (ret + n,stc->text.data,stc->text.size); ret[n] = '\0'; } mail_free_stringlist (&stl);/* either way, done with list */ } return ret; } /* TCP receive line or partial line * Accepts: TCP stream * pointer to return size * pointer to return continuation flag * Returns: text line string, size and continuation flag, or NIL if failure */ static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size, long *contd) { unsigned long n; char *s,*ret,c,d; *contd = NIL; /* assume no continuation */ /* make sure have data */ if (!tcp_getdata (stream)) return NIL; for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) { d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,s,*size = n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } } /* copy partial string from buffer */ memcpy ((ret = (char *) fs_get (n)),s,*size = n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[*size = --n] = '\0'; /* tie off string with null */ } else *contd = LONGT; /* continuation needed */ return ret; } /* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer) { unsigned long n; char *bufptr = buffer; while (size > 0) { /* until request satisfied */ if (!tcp_getdata (stream)) return NIL; n = min (size,stream->ictr);/* number of bytes to transfer */ /* do the copy */ memcpy (bufptr,stream->iptr,n); bufptr += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } bufptr[0] = '\0'; /* tie off string */ return T; } /* TCP/IP receive data * Accepts: TCP/IP stream * Returns: T if success, NIL otherwise */ long tcp_getdata (TCPSTREAM *stream) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); if (stream->tcpsi < 0) return NIL; while (stream->ictr < 1) { /* if nothing in the buffer */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_read; /* read timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */ errno = NIL; /* block and read */ while (((i = select (getdtablesize (),&fds,0,&efds,ttmo_read ? &tmo:0))<0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = socket_read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) && (errno == EINTR)); if (i < 1) return tcp_abort (stream); stream->iptr = stream->ibuf;/* point at TCP buffer */ stream->ictr = i; /* set new byte count */ } return T; } /* TCP/IP send string as record * Accepts: TCP/IP stream * string pointer * Returns: T if success else NIL */ long tcp_soutr (TCPSTREAM *stream,char *string) { return tcp_sout (stream,string,(unsigned long) strlen (string)); } /* TCP/IP send string * Accepts: TCP/IP stream * string pointer * byte count * Returns: T if success else NIL */ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size) { int i; fd_set fds; struct timeval tmo; time_t t = time (0); if (stream->tcpso < 0) return NIL; while (size > 0) { /* until request satisfied */ time_t tl = time (0); /* start of request */ tmo.tv_sec = ttmo_write; /* write timeout */ tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */ errno = NIL; /* block and write */ while (((i = select (getdtablesize (),0,&fds,0,ttmo_write ? &tmo : 0)) < 0) && (errno == EINTR)); if (!i) { /* timeout? */ time_t tc = time (0); if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue; else return tcp_abort (stream); } else if (i < 0) return tcp_abort (stream); while (((i = socket_write (stream->tcpso,string,size)) < 0) && (errno == EINTR)); if (i < 0) return tcp_abort (stream); size -= i; /* how much we sent */ string += i; } return T; /* all done */ } /* TCP/IP close * Accepts: TCP/IP stream */ void tcp_close (TCPSTREAM *stream) { tcp_abort (stream); /* nuke the stream */ /* flush host names */ fs_give ((void **) &stream->host); fs_give ((void **) &stream->localhost); fs_give ((void **) &stream); /* flush the stream */ } /* TCP/IP abort stream * Accepts: TCP/IP stream * Returns: NIL always */ long tcp_abort (TCPSTREAM *stream) { int i; if (stream->tcpsi >= 0) { /* no-op if no socket */ /* nuke the socket */ socket_close (stream->tcpsi); if (stream->tcpsi != stream->tcpso) socket_close (stream->tcpso); stream->tcpsi = stream->tcpso = -1; } return NIL; } /* TCP/IP get host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_host (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP get remote host name * Accepts: TCP/IP stream * Returns: host name for this stream */ char *tcp_remotehost (TCPSTREAM *stream) { return stream->host; /* return host name */ } /* TCP/IP return port for this stream * Accepts: TCP/IP stream * Returns: port number for this stream */ unsigned long tcp_port (TCPSTREAM *stream) { return stream->port; /* return port number */ } /* TCP/IP get local host name * Accepts: TCP/IP stream * Returns: local host name */ char *tcp_localhost (TCPSTREAM *stream) { return stream->localhost; /* return local host name */ } /* Return my local host name * Returns: my local host name */ char *mylocalhost () { char tmp[MAILTMPLEN]; struct hostent *hn; if (!myLocalHost) { /* have local host yet? */ gethostname(tmp,MAILTMPLEN);/* get local host name */ myLocalHost = cpystr ((hn = gethostbyname (tmp)) ? hn->h_name : tmp); } return myLocalHost; } /* TCP/IP return canonical form of host name * Accepts: host name * Returns: canonical form of host name */ char *tcp_canonical (char *name) { char host[MAILTMPLEN]; struct hostent *he; /* look like domain literal? */ if (name[0] == '[' && name[strlen (name) - 1] == ']') return cpystr (name); /* note that Unix requires lowercase! */ else return cpystr ((he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name); } /* TCP/IP get client host name (server calls only) * Returns: client host name */ char *tcp_clienthost () { return "UNKNOWN"; }