char * fdevname(int fd) { char *buf; int error; if (thr_main() != 0) buf = fdevname_buf; else { if (thr_once(&fdevname_init_once, fdevname_keycreate) != 0 || !fdevname_keycreated) return (NULL); if ((buf = thr_getspecific(fdevname_key)) == NULL) { if ((buf = malloc(sizeof fdevname_buf)) == NULL) return (NULL); if (thr_setspecific(fdevname_key, buf) != 0) { free(buf); return (NULL); } } } if (((error = fdevname_r(fd, buf, sizeof fdevname_buf))) != 0) { errno = error; return (NULL); } return (buf); }
char * fdevname(int fd) { static char buf[SPECNAMELEN + 1]; return (fdevname_r(fd, buf, sizeof(buf))); }
int ttyname_r(int fd, char *buf, size_t len) { size_t used; *buf = '\0'; /* Must be a terminal. */ if (!isatty(fd)) return (ENOTTY); /* Must have enough room */ if (len <= sizeof(_PATH_DEV)) return (ERANGE); strcpy(buf, _PATH_DEV); used = strlen(buf); if (fdevname_r(fd, buf + used, len - used) == NULL) return (ENOTTY); return (0); }
intptr_t os_setup_tun(struct openconnect_info *vpninfo) { static char tun_name[80]; int unit_nr = 0; int tun_fd = -1; #if defined(__APPLE__) && defined (HAVE_NET_UTUN_H) /* OS X (since 10.6) can do this as well as the traditional BSD devices supported via tuntaposx. */ struct sockaddr_ctl sc; struct ctl_info ci; if (vpninfo->ifname) { char *endp = NULL; if (!strncmp(vpninfo->ifname, "tun", 3)) goto do_bsdtun; if (strncmp(vpninfo->ifname, "utun", 4) || (unit_nr = strtol(vpninfo->ifname + 4, &endp, 10), !endp) || (unit_nr && vpninfo->ifname[4] == '0') || *endp) { vpn_progress(vpninfo, PRG_ERR, _("Invalid interface name '%s'; must match 'utun%%d' or 'tun%%d'\n"), vpninfo->ifname); return -EINVAL; } } tun_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); if (tun_fd < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to open SYSPROTO_CONTROL socket: %s\n"), strerror(errno)); goto utun_fail; } snprintf(ci.ctl_name, sizeof(ci.ctl_name), UTUN_CONTROL_NAME); if (ioctl(tun_fd, CTLIOCGINFO, &ci) == -1) { vpn_progress(vpninfo, PRG_ERR, _("Failed to query utun control id: %s\n"), strerror(errno)); close(tun_fd); goto utun_fail; } sc.sc_id = ci.ctl_id; sc.sc_len = sizeof(sc); sc.sc_family = AF_SYSTEM; sc.ss_sysaddr = AF_SYS_CONTROL; do { sc.sc_unit = unit_nr + 1; if (!connect(tun_fd, (struct sockaddr * )&sc, sizeof(sc))) { if (!vpninfo->ifname && asprintf(&vpninfo->ifname, "utun%d", unit_nr) == -1) { vpn_progress(vpninfo, PRG_ERR, _("Failed to allocate utun device name\n")); close(tun_fd); goto utun_fail; } return tun_fd; } unit_nr++; } while (sc.sc_unit < 255 && !vpninfo->ifname); vpn_progress(vpninfo, PRG_ERR, _("Failed to connect utun unit: %s\n"), strerror(errno)); close(tun_fd); utun_fail: /* If we were explicitly asked for a utun device, fail. Else try tuntaposx */ if (vpninfo->ifname) return -EIO; tun_fd = -1; do_bsdtun: #endif /* __APPLE__ && HAVE_NET_UTUN_H */ if (vpninfo->ifname) { char *endp = NULL; if (strncmp(vpninfo->ifname, "tun", 3) || ((void)strtol(vpninfo->ifname + 3, &endp, 10), !endp) || *endp) { vpn_progress(vpninfo, PRG_ERR, _("Invalid interface name '%s'; must match 'tun%%d'\n"), vpninfo->ifname); return -EINVAL; } snprintf(tun_name, sizeof(tun_name), "/dev/%s", vpninfo->ifname); tun_fd = bsd_open_tun(tun_name); if (tun_fd < 0) { int err = errno; vpn_progress(vpninfo, PRG_ERR, _("Cannot open '%s': %s\n"), tun_name, strerror(err)); return -EINVAL; } } #ifdef HAVE_FDEVNAME_R /* We don't have to iterate over the possible devices; on FreeBSD at least, opening /dev/tun will give us the next available device. */ if (tun_fd < 0) { tun_fd = open("/dev/tun", O_RDWR); if (tun_fd >= 0) { if (!fdevname_r(tun_fd, tun_name, sizeof(tun_name)) || strncmp(tun_name, "tun", 3)) { close(tun_fd); tun_fd = -1; } else vpninfo->ifname = strdup(tun_name); } } #endif if (tun_fd < 0) { for (unit_nr = 0; unit_nr < 255; unit_nr++) { sprintf(tun_name, "/dev/tun%d", unit_nr); tun_fd = bsd_open_tun(tun_name); if (tun_fd >= 0) break; } if (tun_fd < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to open tun device: %s\n"), strerror(errno)); return -EIO; } vpninfo->ifname = strdup(tun_name + 5); } #ifdef TUNSIFHEAD unit_nr = 1; if (ioctl(tun_fd, TUNSIFHEAD, &unit_nr) < 0) { vpn_perror(vpninfo, _("TUNSIFHEAD")); close(tun_fd); return -EIO; } #endif /* Ancient vpnc-scripts might not get this right */ set_tun_mtu(vpninfo); return tun_fd; }
intptr_t os_setup_tun(struct openconnect_info *vpninfo) { int tun_fd = -1; #ifdef IFF_TUN /* Linux */ struct ifreq ifr; int tunerr; tun_fd = open("/dev/net/tun", O_RDWR); if (tun_fd < 0) { /* Android has /dev/tun instead of /dev/net/tun Since other systems might have too, just try it as a fallback instead of using ifdef __ANDROID__ */ tunerr = errno; tun_fd = open("/dev/tun", O_RDWR); } if (tun_fd < 0) { /* If the error on /dev/tun is ENOENT, that's boring. Use the error we got on /dev/net/tun instead */ if (errno != ENOENT) tunerr = errno; vpn_progress(vpninfo, PRG_ERR, _("Failed to open tun device: %s\n"), strerror(tunerr)); return -EIO; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; if (vpninfo->ifname) strncpy(ifr.ifr_name, vpninfo->ifname, sizeof(ifr.ifr_name) - 1); if (ioctl(tun_fd, TUNSETIFF, (void *) &ifr) < 0) { vpn_progress(vpninfo, PRG_ERR, _("TUNSETIFF failed: %s\n"), strerror(errno)); close(tun_fd); return -EIO; } if (!vpninfo->ifname) vpninfo->ifname = strdup(ifr.ifr_name); #elif defined(__sun__) static char tun_name[80]; int unit_nr; tun_fd = open("/dev/tun", O_RDWR); if (tun_fd < 0) { perror(_("open /dev/tun")); return -EIO; } unit_nr = ioctl(tun_fd, TUNNEWPPA, -1); if (unit_nr < 0) { perror(_("Failed to create new tun")); close(tun_fd); return -EIO; } if (ioctl(tun_fd, I_SRDOPT, RMSGD) < 0) { perror(_("Failed to put tun file descriptor into message-discard mode")); close(tun_fd); return -EIO; } sprintf(tun_name, "tun%d", unit_nr); vpninfo->ifname = strdup(tun_name); vpninfo->ip_fd = link_proto(unit_nr, "/dev/udp", IFF_IPV4); if (vpninfo->ip_fd < 0) { close(tun_fd); return -EIO; } if (vpninfo->ip_info.addr6) { vpninfo->ip6_fd = link_proto(unit_nr, "/dev/udp6", IFF_IPV6); if (vpninfo->ip6_fd < 0) { close(tun_fd); close(vpninfo->ip_fd); vpninfo->ip_fd = -1; return -EIO; } } else vpninfo->ip6_fd = -1; #else /* BSD et al have /dev/tun$x devices */ static char tun_name[80]; int i; if (vpninfo->ifname) { char *endp = NULL; if (strncmp(vpninfo->ifname, "tun", 3) || ((void)strtol(vpninfo->ifname + 3, &endp, 10), !endp) || *endp) { vpn_progress(vpninfo, PRG_ERR, _("Invalid interface name '%s'; must match 'tun%%d'\n"), vpninfo->ifname); return -EINVAL; } snprintf(tun_name, sizeof(tun_name), "/dev/%s", vpninfo->ifname); tun_fd = bsd_open_tun(tun_name); if (tun_fd < 0) { int err = errno; vpn_progress(vpninfo, PRG_ERR, _("Cannot open '%s': %s\n"), tun_name, strerror(err)); return -EINVAL; } } #ifdef HAVE_FDEVNAME_R /* We don't have to iterate over the possible devices; on FreeBSD at least, opening /dev/tun will give us the next available device. */ if (tun_fd < 0) { tun_fd = open("/dev/tun", O_RDWR); if (tun_fd >= 0) { if (!fdevname_r(tun_fd, tun_name, sizeof(tun_name)) || strncmp(tun_name, "tun", 3)) { close(tun_fd); tun_fd = -1; } else vpninfo->ifname = strdup(tun_name); } } #endif if (tun_fd < 0) { for (i = 0; i < 255; i++) { sprintf(tun_name, "/dev/tun%d", i); tun_fd = bsd_open_tun(tun_name); if (tun_fd >= 0) break; } if (tun_fd < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to open tun device: %s\n"), strerror(errno)); return -EIO; } vpninfo->ifname = strdup(tun_name + 5); } #ifdef TUNSIFHEAD i = 1; if (ioctl(tun_fd, TUNSIFHEAD, &i) < 0) { perror(_("TUNSIFHEAD")); close(tun_fd); return -EIO; } #endif #endif /* BSD-style */ /* Ancient vpnc-scripts might not get this right */ set_tun_mtu(vpninfo); return tun_fd; }
int main(int argc, char *argv[]) { struct stat st; int ch, rc, errs, am_readlink; int lsF, fmtchar, usestat, fn, nonl, quiet; const char *statfmt, *options, *synopsis; char dname[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV; const char *file; am_readlink = 0; lsF = 0; fmtchar = '\0'; usestat = 0; nonl = 0; quiet = 0; linkfail = 0; statfmt = NULL; timefmt = NULL; if (strcmp(getprogname(), "readlink") == 0) { am_readlink = 1; options = "fn"; synopsis = "[-fn] [file ...]"; statfmt = "%Y"; fmtchar = 'f'; quiet = 1; } else { options = "f:FlLnqrst:x"; synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] " "[-t timefmt] [file ...]"; } while ((ch = getopt(argc, argv, options)) != -1) switch (ch) { case 'F': lsF = 1; break; case 'L': usestat = 1; break; case 'n': nonl = 1; break; case 'q': quiet = 1; break; case 'f': if (am_readlink) { statfmt = "%R"; break; } statfmt = optarg; /* FALLTHROUGH */ case 'l': case 'r': case 's': case 'x': if (fmtchar != 0) errx(1, "can't use format '%c' with '%c'", fmtchar, ch); fmtchar = ch; break; case 't': timefmt = optarg; break; default: usage(synopsis); } argc -= optind; argv += optind; fn = 1; if (fmtchar == '\0') { if (lsF) fmtchar = 'l'; else { fmtchar = 'f'; statfmt = DEF_FORMAT; } } if (lsF && fmtchar != 'l') errx(1, "can't use format '%c' with -F", fmtchar); switch (fmtchar) { case 'f': /* statfmt already set */ break; case 'l': statfmt = lsF ? LSF_FORMAT : LS_FORMAT; break; case 'r': statfmt = RAW_FORMAT; break; case 's': statfmt = SHELL_FORMAT; break; case 'x': statfmt = LINUX_FORMAT; if (timefmt == NULL) timefmt = "%c"; break; default: usage(synopsis); /*NOTREACHED*/ } if (timefmt == NULL) timefmt = TIME_FORMAT; errs = 0; do { if (argc == 0) { if (fdevname_r(STDIN_FILENO, dname + sizeof _PATH_DEV - 1, SPECNAMELEN) != NULL) file = dname; else file = "(stdin)"; rc = fstat(STDIN_FILENO, &st); } else { file = argv[0]; if (usestat) { /* * Try stat() and if it fails, fall back to * lstat() just in case we're examining a * broken symlink. */ if ((rc = stat(file, &st)) == -1 && errno == ENOENT && (rc = lstat(file, &st)) == -1) errno = ENOENT; } else rc = lstat(file, &st); } if (rc == -1) { errs = 1; linkfail = 1; if (!quiet) warn("%s: stat", file); } else output(&st, file, statfmt, fn, nonl); argv++; argc--; fn++; } while (argc > 0); return (am_readlink ? linkfail : errs); }