///////////////////////////////////////////////////////////////////////// // $Id$ ///////////////////////////////////////////////////////////////////////// /* * QEMU compatibility functions * * Copyright (c) 2003-2008 Fabrice Bellard * Copyright (C) 2014-2017 The Bochs Project * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "slirp.h" #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #if BX_NETWORKING && BX_NETMOD_SLIRP void pstrcpy(char *buf, int buf_size, const char *str) { int c; char *q = buf; if (buf_size <= 0) return; for(;;) { c = *str++; if (c == 0 || q >= buf + buf_size - 1) break; *q++ = c; } *q = '\0'; } void qemu_set_nonblock(int fd) { #ifndef _WIN32 int f; f = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, f | O_NONBLOCK); #elif !defined(_MSC_VER) ULONG opt = 1; ioctlsocket(fd, FIONBIO, &opt); #endif } #ifndef HAVE_INET_ATON int inet_aton(const char *cp, struct in_addr *ia) { uint32_t addr = inet_addr(cp); if (addr == 0xffffffff) { return 0; } ia->s_addr = addr; return 1; } #endif int socket_set_fast_reuse(int fd) { #ifndef _WIN32 int val = 1, ret; ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); assert(ret == 0); return ret; #else return 0; #endif } int socket_set_nodelay(int fd) { int v = 1; return qemu_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); } void qemu_set_cloexec(int fd) { #ifndef _WIN32 int f; f = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, f | FD_CLOEXEC); #endif } /* * Opens a socket with FD_CLOEXEC set */ int qemu_socket(int domain, int type, int protocol) { int ret; #ifdef SOCK_CLOEXEC ret = socket(domain, type | SOCK_CLOEXEC, protocol); if (ret != -1 || errno != EINVAL) { return ret; } #endif ret = socket(domain, type, protocol); if (ret >= 0) { qemu_set_cloexec(ret); } return ret; } #if !defined(_WIN32) && !defined(__CYGWIN__) #define CONFIG_SMBD_COMMAND "/usr/sbin/smbd" #include /* automatic user mode samba server configuration */ void slirp_smb_cleanup(Slirp *s, char *smb_tmpdir) { char cmd[128]; char error_msg[256]; int ret; if (smb_tmpdir[0] != '\0') { snprintf(cmd, sizeof(cmd), "rm -rf %s", smb_tmpdir); ret = system(cmd); if (ret == -1 || !WIFEXITED(ret)) { snprintf(error_msg, sizeof(error_msg), "'%s' failed.", cmd); slirp_warning(s, error_msg); } else if (WEXITSTATUS(ret)) { snprintf(error_msg, sizeof(error_msg), "'%s' failed. Error code: %d", cmd, WEXITSTATUS(ret)); slirp_warning(s, error_msg); } smb_tmpdir[0] = '\0'; } } int slirp_smb(Slirp *s, char *smb_tmpdir, const char *exported_dir, struct in_addr vserver_addr) { static int instance; int i; char smb_conf[128], smb_cmdline[128]; char share[64], error_msg[256]; struct passwd *passwd; FILE *f; passwd = getpwuid(geteuid()); if (!passwd) { sprintf(error_msg, "failed to retrieve user name"); slirp_warning(s, error_msg); return -1; } if (access(CONFIG_SMBD_COMMAND, F_OK)) { sprintf(error_msg, "could not find '%s', please install it", CONFIG_SMBD_COMMAND); slirp_warning(s, error_msg); return -1; } if (access(exported_dir, R_OK | X_OK)) { snprintf(error_msg, sizeof(error_msg), "error accessing shared directory '%s': %s", exported_dir, strerror(errno)); slirp_warning(s, error_msg); return -1; } i = strlen(exported_dir) - 2; while ((i > 0) && (exported_dir[i] != '/')) i--; snprintf(share, sizeof(share), "%s", &exported_dir[i+1]); if (share[strlen(share)-1] == '/') share[strlen(share)-1] = '\0'; snprintf(smb_tmpdir, 128, "/tmp/bochs-smb.%ld-%d", (long)getpid(), instance++); if (mkdir(smb_tmpdir, 0700) < 0) { snprintf(error_msg, sizeof(error_msg), "could not create samba server dir '%s'", smb_tmpdir); slirp_warning(s, error_msg); return -1; } snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_tmpdir, "smb.conf"); f = fopen(smb_conf, "w"); if (!f) { slirp_smb_cleanup(s, smb_tmpdir); snprintf(error_msg, sizeof(error_msg), "could not create samba server configuration file '%s'", smb_conf); slirp_warning(s, error_msg); return -1; } fprintf(f, "[global]\n" "private dir=%s\n" "socket address=127.0.0.1\n" "pid directory=%s\n" "lock directory=%s\n" "state directory=%s\n" "log file=%s/log.smbd\n" "smb passwd file=%s/smbpasswd\n" "security = user\n" "map to guest = Bad User\n" "[%s]\n" "path=%s\n" "read only=no\n" "guest ok=yes\n" "force user=%s\n", smb_tmpdir, smb_tmpdir, smb_tmpdir, smb_tmpdir, smb_tmpdir, smb_tmpdir, share, exported_dir, passwd->pw_name ); fclose(f); snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s", CONFIG_SMBD_COMMAND, smb_conf); if (slirp_add_exec(s, 0, smb_cmdline, &vserver_addr, 139) < 0 || slirp_add_exec(s, 0, smb_cmdline, &vserver_addr, 445) < 0) { slirp_smb_cleanup(s, smb_tmpdir); sprintf(error_msg, "conflicting/invalid smbserver address"); slirp_warning(s, error_msg); return -1; } return 0; } #endif static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { const char *p, *p1; int len; p = *pp; p1 = strchr(p, sep); if (!p1) return -1; len = p1 - p; p1++; if (buf_size > 0) { if (len > buf_size - 1) len = buf_size - 1; memcpy(buf, p, len); buf[len] = '\0'; } *pp = p1; return 0; } int slirp_hostfwd(Slirp *s, const char *redir_str, int legacy_format) { struct in_addr host_addr; struct in_addr guest_addr; int host_port, guest_port; const char *p; char buf[256], error_msg[256]; int is_udp; char *end; host_addr.s_addr = INADDR_ANY; guest_addr.s_addr = 0; p = redir_str; if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { goto fail_syntax; } if (!strcmp(buf, "tcp") || buf[0] == '\0') { is_udp = 0; } else if (!strcmp(buf, "udp")) { is_udp = 1; } else { goto fail_syntax; } if (!legacy_format) { if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { goto fail_syntax; } if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { goto fail_syntax; } } if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) { goto fail_syntax; } host_port = strtol(buf, &end, 0); if (*end != '\0' || host_port < 1 || host_port > 65535) { goto fail_syntax; } if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { goto fail_syntax; } if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { goto fail_syntax; } guest_port = strtol(p, &end, 0); if (*end != '\0' || guest_port < 1 || guest_port > 65535) { goto fail_syntax; } if (slirp_add_hostfwd(s, is_udp, host_addr, host_port, guest_addr, guest_port) < 0) { sprintf(error_msg, "could not set up host forwarding rule '%s'", redir_str); slirp_warning(s, error_msg); return -1; } return 0; fail_syntax: sprintf(error_msg, "invalid host forwarding rule '%s'", redir_str); slirp_warning(s, error_msg); return -1; } #endif