diff options
Diffstat (limited to 'imap/src/osdep/vms/tcp_vmsm.c')
-rw-r--r-- | imap/src/osdep/vms/tcp_vmsm.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/imap/src/osdep/vms/tcp_vmsm.c b/imap/src/osdep/vms/tcp_vmsm.c new file mode 100644 index 00000000..bc9d9ca7 --- /dev/null +++ b/imap/src/osdep/vms/tcp_vmsm.c @@ -0,0 +1,479 @@ +/* ======================================================================== + * 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 + * + * + * ======================================================================== + */ + +/* + * Program: VMS TCP/IP routines for Multinet + * + * Author: Mark Crispin + * Networks and Distributed Computing + * Computing & Communications + * University of Washington + * Administration Building, AG-44 + * Seattle, WA 98195 + * Internet: MRC@CAC.Washington.EDU + * + * Date: 2 August 1994 + * Last Edited: 13 January 2008 + */ + + +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 name; + /* note that Unix requires lowercase! */ + else return (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"; +} |