diff --git a/io.c b/io.c index 2a5c2e7..80a5b88 100644 --- a/io.c +++ b/io.c @@ -279,7 +279,17 @@ static RECVALUE (*matchrec)(IOBUF *iop, struct recmatch *recm, SCANSTATE *state) static int get_a_record(char **out, IOBUF *iop, int *errcode); static void free_rp(struct redirect *rp); -static int inetfile(const char *str, int *length, int *family); + +struct inet_socket_info { + int family; /* AF_UNSPEC, AF_INET, or AF_INET6 */ + int protocol; /* SOCK_STREAM or SOCK_DGRAM */ + struct { + int offset; + int len; + } localport, remotehost, remoteport; +}; + +static int inetfile(const char *str, struct inet_socket_info *isn); static NODE *in_PROCINFO(const char *pidx1, const char *pidx2, NODE **full_idx); static long get_read_timeout(IOBUF *iop); @@ -772,10 +782,13 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) * Use /inet4 to force IPv4, /inet6 to force IPv6, and plain * /inet will be whatever we get back from the system. */ - if (inetfile(str, & len, NULL)) { - tflag |= RED_SOCKET; - if (strncmp(str + len, "tcp/", 4) == 0) - tflag |= RED_TCP; /* use shutdown when closing */ + { + struct inet_socket_info isi; + if (inetfile(str, & isi)) { + tflag |= RED_SOCKET; + if (isi.protocol == SOCK_STREAM) + tflag |= RED_TCP; /* use shutdown when closing */ + } } #endif /* HAVE_SOCKETS */ @@ -901,7 +914,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) direction = "to/from"; if (! two_way_open(str, rp)) { #ifdef HAVE_SOCKETS - if (inetfile(str, NULL, NULL)) { + if (inetfile(str, NULL)) { *errflg = errno; /* do not free rp, saving it for reuse (save_rp = rp) */ return NULL; @@ -1540,6 +1553,7 @@ devopen(const char *name, const char *mode) int flag = 0; int len; int family; + struct inet_socket_info isi; if (strcmp(name, "-") == 0) return fileno(stdin); @@ -1576,74 +1590,14 @@ devopen(const char *name, const char *mode) /* do not set close-on-exec for inherited fd's */ if (openfd != INVALID_HANDLE) return openfd; - } else if (inetfile(name, & len, & family)) { + } else if (inetfile(name, & isi)) { #ifdef HAVE_SOCKETS - /* /inet/protocol/localport/hostname/remoteport */ - int protocol; - char *hostname; - char *hostnameslastcharp; - char *localpname; - char *localpnamelastcharp; - - cp = (char *) name + len; - /* which protocol? */ - if (strncmp(cp, "tcp/", 4) == 0) - protocol = SOCK_STREAM; - else if (strncmp(cp, "udp/", 4) == 0) - protocol = SOCK_DGRAM; - else { - protocol = SOCK_STREAM; /* shut up the compiler */ - fatal(_("no (known) protocol supplied in special filename `%s'"), - name); - } - cp += 4; - - /* which localport? */ - localpname = cp; - while (*cp != '/' && *cp != '\0') - cp++; - /* - * Require a port, let them explicitly put 0 if - * they don't care. - */ - if (*cp != '/' || cp == localpname) - fatal(_("special file name `%s' is incomplete"), name); - - /* - * We change the special file name temporarily because we - * need a 0-terminated string here for conversion with atoi(). - * By using atoi() the use of decimal numbers is enforced. - */ - *cp = '\0'; - localpnamelastcharp = cp; - - /* which hostname? */ - cp++; - hostname = cp; - while (*cp != '/' && *cp != '\0') - cp++; - if (*cp != '/' || cp == hostname) { - *localpnamelastcharp = '/'; - fatal(_("must supply a remote hostname to `/inet'")); - } - *cp = '\0'; - hostnameslastcharp = cp; + cp = (char *) name; - /* which remoteport? */ - cp++; - /* - * The remote port ends the special file name. - * This means there already is a '\0' at the end of the string. - * Therefore no need to patch any string ending. - * - * Here too, require a port, let them explicitly put 0 if - * they don't care. - */ - if (*cp == '\0') { - *localpnamelastcharp = '/'; - *hostnameslastcharp = '/'; - fatal(_("must supply a remote port to `/inet'")); - } + /* socketopen requires nul-terminated strings */ + cp[isi.localport.offset+isi.localport.len] = '\0'; + cp[isi.remotehost.offset+isi.remotehost.len] = '\0'; + /* remoteport comes last, so already nul-terminated */ { #define DEFAULT_RETRIES 20 @@ -1680,13 +1634,14 @@ devopen(const char *name, const char *mode) retries = def_retries; do { - openfd = socketopen(family, protocol, localpname, cp, hostname); + openfd = socketopen(isi.family, isi.protocol, name+isi.localport.offset, name+isi.remoteport.offset, name+isi.remotehost.offset); retries--; } while (openfd == INVALID_HANDLE && retries > 0 && usleep(msleep) == 0); } - *localpnamelastcharp = '/'; - *hostnameslastcharp = '/'; + /* restore original name string */ + cp[isi.localport.offset+isi.localport.len] = '/'; + cp[isi.remotehost.offset+isi.remotehost.len] = '/'; #else /* ! HAVE_SOCKETS */ fatal(_("TCP/IP communications are not supported")); #endif /* HAVE_SOCKETS */ @@ -1700,9 +1655,8 @@ strictopen: /* On OS/2 and Windows directory access via open() is not permitted. */ struct stat buf; - int l, f; - if (!inetfile(name, &l, &f) + if (!inetfile(name, NULL) && stat(name, & buf) == 0 && S_ISDIR(buf.st_mode)) errno = EISDIR; } @@ -1724,7 +1678,7 @@ two_way_open(const char *str, struct redirect *rp) #ifdef HAVE_SOCKETS /* case 1: socket */ - if (inetfile(str, NULL, NULL)) { + if (inetfile(str, NULL)) { int fd, newfd; fd = devopen(str, "rw"); @@ -3751,34 +3705,87 @@ free_rp(struct redirect *rp) /* inetfile --- return true for a /inet special file, set other values */ static int -inetfile(const char *str, int *length, int *family) +inetfile(const char *str, struct inet_socket_info *isi) { - bool ret = false; - - if (strncmp(str, "/inet/", 6) == 0) { - ret = true; - if (length != NULL) - *length = 6; - if (family != NULL) - *family = AF_UNSPEC; - } else if (strncmp(str, "/inet4/", 7) == 0) { - ret = true; - if (length != NULL) - *length = 7; - if (family != NULL) - *family = AF_INET; - } else if (strncmp(str, "/inet6/", 7) == 0) { - ret = true; - if (length != NULL) - *length = 7; - if (family != NULL) - *family = AF_INET6; + const char *cp = str; + struct inet_socket_info buf; + + /* syntax: /inet/protocol/localport/hostname/remoteport */ + if (strncmp(cp, "/inet", 5) != 0) + /* quick exit */ + return false; + if (! isi) + isi = & buf; + cp += 5; + switch (*cp) { + case '/': + isi->family = AF_UNSPEC; + break; + case '4': + if (*++cp != '/') + return false; + isi->family = AF_INET; + break; + case '6': + if (*++cp != '/') + return false; + isi->family = AF_INET6; + break; + default: + return false; + } + cp++; /* skip past '/' */ + + /* which protocol? */ + if (strncmp(cp, "tcp/", 4) == 0) + isi->protocol = SOCK_STREAM; + else if (strncmp(cp, "udp/", 4) == 0) + isi->protocol = SOCK_DGRAM; + else + return false; + cp += 4; + + /* which localport? */ + isi->localport.offset = cp-str; + while (*cp != '/' && *cp != '\0') + cp++; + /* + * Require a port, let them explicitly put 0 if + * they don't care. + */ + if (*cp != '/' || ((isi->localport.len = (cp-str)-isi->localport.offset) == 0)) + return false; + + /* which hostname? */ + cp++; + isi->remotehost.offset = cp-str; + while (*cp != '/' && *cp != '\0') + cp++; + if (*cp != '/' || ((isi->remotehost.len = (cp-str)-isi->remotehost.offset) == 0)) + return false; + + /* which remoteport? */ + cp++; + /* + * The remote port ends the special file name. + * This means there already is a '\0' at the end of the string. + * Therefore no need to patch any string ending. + * + * Here too, require a port, let them explicitly put 0 if + * they don't care. + */ + isi->remoteport.offset = cp-str; + while (*cp != '/' && *cp != '\0') + cp++; + if (*cp != '\0' || ((isi->remoteport.len = (cp-str)-isi->remoteport.offset) == 0)) + return false; + #ifndef HAVE_GETADDRINFO + /* final check for IPv6: */ + if (isi->family == AF_INET6) fatal(_("IPv6 communication is not supported")); #endif - } - - return ret; + return true; } /*