Bochs/bochs/iodev/network/slirp/compat.cc

350 lines
9.1 KiB
C++
Raw Normal View History

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
/*
* QEMU compatibility functions
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (C) 2014-2020 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 <pwd.h>
/* 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[150];
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"
"interfaces=127.0.0.1\n"
"bind interfaces only=yes\n"
"pid directory=%s\n"
"lock directory=%s\n"
"state directory=%s\n"
"cache directory=%s\n"
"ncalrpc dir=%s/ncalrpc\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,
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