Proper IPv6 and dual stack networking

This means that rigctl & rigctld  now work with default arguments on a
modern Windows  machine with dual  stack and localhost being  [::1] as
the first interface returned by getaddrinfo().

Try all the interfaces return by DNS lookups to establish a connection
or listening  port. Handle  Windows network  errors correctly  so that
meaningful messages are printed.

The rigctl program now accepts  IPv6 numeric addresses in the portname
field like [<addr>}:<port> for example  the IPv6 loopback on port 4531
would be [::1]:4531.
libusb-1-0
Bill Somerville 2015-11-27 12:59:21 +00:00
rodzic a4a93ac044
commit 181a3e9697
2 zmienionych plików z 202 dodań i 59 usunięć

Wyświetl plik

@ -73,6 +73,36 @@
static int wsstarted; static int wsstarted;
#endif #endif
static void handle_error (enum rig_debug_level_e lvl, const char *msg)
{
int e;
#ifdef __MINGW32__
LPVOID lpMsgBuf;
lpMsgBuf = (LPVOID)"Unknown error";
e = WSAGetLastError();
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, e,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// Default language
(LPTSTR)&lpMsgBuf, 0, NULL))
{
rig_debug (lvl, "%s: Network error %d: %s\n", msg, e, lpMsgBuf);
LocalFree(lpMsgBuf);
}
else
{
rig_debug (lvl, "%s: Network error %d\n", msg, e);
}
#else
e = errno;
rig_debug (lvl, "%s: Network error %d: %s\n", msg, e, strerror (e));
#endif
}
/** /**
* \brief Open network port using rig.state data * \brief Open network port using rig.state data
* *
@ -87,9 +117,9 @@ int network_open(hamlib_port_t *rp, int default_port)
{ {
int fd; /* File descriptor for the port */ int fd; /* File descriptor for the port */
int status; int status;
struct addrinfo hints, *res; struct addrinfo hints, *res, *saved_res;
char *portstr; char *hoststr, *portstr, *bracketstr1, *bracketstr2;
char hostname[FILPATHLEN] = "127.0.0.1"; char hostname[FILPATHLEN];
char defaultportstr[8]; char defaultportstr[8];
#ifdef __MINGW32__ #ifdef __MINGW32__
@ -110,48 +140,90 @@ int network_open(hamlib_port_t *rp, int default_port)
else else
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
if (rp->pathname[0] == ':') { hoststr = NULL; /* default of all local interfaces */
portstr = rp->pathname+1; if (rp->pathname && rp->pathname[0] == ':')
} else { {
portstr = rp->pathname + 1;
}
else
{
if (strlen (rp->pathname))
{
strncpy(hostname, rp->pathname, FILPATHLEN-1); strncpy(hostname, rp->pathname, FILPATHLEN-1);
hoststr = hostname;
/* search last ':', because IPv6 may have some */ /* look for IPv6 numeric form [<addr>] */
portstr = strrchr(hostname, ':'); bracketstr1 = strchr(hoststr, '[');
if (portstr) { bracketstr2 = strrchr(hoststr, ']');
if (bracketstr1 && bracketstr2 && bracketstr2 > bracketstr1)
{
hoststr = bracketstr1 + 1;
*bracketstr2 = '\0';
portstr = bracketstr2 + 1; /* possible port after ]: */
}
else
{
bracketstr2 = NULL;
portstr = hoststr; /* possible port after : */
}
/* search last ':' */
portstr = strrchr(portstr, ':');
if (portstr)
{
*portstr++ = '\0'; *portstr++ = '\0';
} else { }
else
{
sprintf(defaultportstr, "%d", default_port); sprintf(defaultportstr, "%d", default_port);
portstr = defaultportstr; portstr = defaultportstr;
} }
} }
}
status=getaddrinfo(hostname, portstr, &hints, &res); status=getaddrinfo(hoststr, portstr, &hints, &res);
if (status != 0) { if (status != 0) {
rig_debug(RIG_DEBUG_ERR, "Cannot get host \"%s\": %s\n", rig_debug(RIG_DEBUG_ERR, "Cannot get host \"%s\": %s\n",
rp->pathname, gai_strerror(errno)); rp->pathname, gai_strerror(errno));
return -RIG_ECONF; return -RIG_ECONF;
} }
saved_res = res;
/* we don't want a signal when connection get broken */ /* we don't want a signal when connection get broken */
#ifdef SIGPIPE #ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#endif #endif
do
{
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0) if (fd < 0)
{
handle_error (RIG_DEBUG_ERR, "socket");
freeaddrinfo (saved_res);
return -RIG_EIO; return -RIG_EIO;
}
status = connect(fd, res->ai_addr, res->ai_addrlen); if ((status = connect(fd, res->ai_addr, res->ai_addrlen)) == 0)
freeaddrinfo(res); {
if (status < 0) { break;
rig_debug(RIG_DEBUG_ERR, "Cannot open NET device \"%s\": %s\n", }
rp->pathname, strerror(errno)); handle_error (RIG_DEBUG_WARN, "connect (trying next interface)");
close(fd);
#ifdef __MINGW32__
closesocket (fd);
#else
close (fd);
#endif
} while ((res = res->ai_next) != NULL);
freeaddrinfo (saved_res);
if (NULL == res) {
rig_debug (RIG_DEBUG_ERR, "Failed to connect to %s\n"
, rp->pathname ? rp->pathname : "localhost:4532");
return -RIG_EIO; return -RIG_EIO;
} }
rp->fd = fd; rp->fd = fd;
return RIG_OK; return RIG_OK;
} }

Wyświetl plik

@ -102,7 +102,7 @@ static struct option long_options[] =
struct handle_data { struct handle_data {
RIG *rig; RIG *rig;
int sock; int sock;
struct sockaddr_in cli_addr; struct sockaddr_storage cli_addr;
socklen_t clilen; socklen_t clilen;
}; };
@ -120,6 +120,36 @@ const char *src_addr = NULL; /* INADDR_ANY */
#define MAXCONFLEN 128 #define MAXCONFLEN 128
static void handle_error (enum rig_debug_level_e lvl, const char *msg)
{
int e;
#ifdef __MINGW32__
LPVOID lpMsgBuf;
lpMsgBuf = (LPVOID)"Unknown error";
e = WSAGetLastError();
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, e,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// Default language
(LPTSTR)&lpMsgBuf, 0, NULL))
{
rig_debug (lvl, "%s: Network error %d: %s\n", msg, e, lpMsgBuf);
LocalFree(lpMsgBuf);
}
else
{
rig_debug (lvl, "%s: Network error %d\n", msg, e);
}
#else
e = errno;
rig_debug (lvl, "%s: Network error %d: %s\n", msg, e, strerror (e));
#endif
}
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
RIG *my_rig; /* handle to rig (instance) */ RIG *my_rig; /* handle to rig (instance) */
@ -137,9 +167,12 @@ int main (int argc, char *argv[])
char *civaddr = NULL; /* NULL means no need to set conf */ char *civaddr = NULL; /* NULL means no need to set conf */
char conf_parms[MAXCONFLEN] = ""; char conf_parms[MAXCONFLEN] = "";
struct addrinfo hints, *result; int sockopt;
struct addrinfo hints, *result, *saved_result;
int sock_listen; int sock_listen;
int reuseaddr = 1; int reuseaddr = 1;
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
while(1) { while(1) {
int c; int c;
@ -370,7 +403,7 @@ int main (int argc, char *argv[])
exit(1); exit(1);
} }
int sockopt = SO_SYNCHRONOUS_NONALERT; sockopt = SO_SYNCHRONOUS_NONALERT;
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sockopt, sizeof(sockopt)); setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sockopt, sizeof(sockopt));
#endif #endif
@ -388,28 +421,56 @@ int main (int argc, char *argv[])
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retcode)); fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retcode));
exit(2); exit(2);
} }
saved_result = result;
do
{
sock_listen = socket(result->ai_family, result->ai_socktype, sock_listen = socket(result->ai_family, result->ai_socktype,
result->ai_protocol); result->ai_protocol);
if (sock_listen < 0) { if (sock_listen < 0) {
perror("ERROR opening socket"); handle_error (RIG_DEBUG_ERR, "socket");
freeaddrinfo(saved_result); /* No longer needed */
exit(2); exit(2);
} }
if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuseaddr, sizeof(reuseaddr)) < 0) { (char *)&reuseaddr, sizeof(reuseaddr)) < 0) {
rig_debug(RIG_DEBUG_ERR, "setsockopt: %s\n", strerror(errno)); handle_error (RIG_DEBUG_ERR, "setsockopt");
exit (1); freeaddrinfo(saved_result); /* No longer needed */
}
if (bind(sock_listen, result->ai_addr, result->ai_addrlen) < 0) {
rig_debug(RIG_DEBUG_ERR, "binding: %s\n", strerror(errno));
exit (1); exit (1);
} }
freeaddrinfo(result); /* No longer needed */ #ifdef __MINGW32__
/* allow IPv4 mapped to IPv6 clients, MS default this to 1! */
sockopt = 0;
if (setsockopt(sock_listen, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&sockopt, sizeof(sockopt)) < 0) {
handle_error (RIG_DEBUG_ERR, "setsockopt");
freeaddrinfo(saved_result); /* No longer needed */
exit (1);
}
#endif
if (0 == bind(sock_listen, result->ai_addr, result->ai_addrlen)) {
break;
}
handle_error (RIG_DEBUG_WARN, "binding failed (trying next interface)");
#ifdef __MINGW32__
closesocket (sock_listen);
#else
close (sock_listen);
#endif
} while ((result = result->ai_next) != NULL);
freeaddrinfo(saved_result); /* No longer needed */
if (NULL == result)
{
rig_debug(RIG_DEBUG_ERR, "bind error - no available interface\n");
exit (1);
}
if (listen(sock_listen, 4) < 0) { if (listen(sock_listen, 4) < 0) {
rig_debug(RIG_DEBUG_ERR, "listening: %s\n", strerror(errno)); handle_error (RIG_DEBUG_ERR, "listening");
exit (1); exit (1);
} }
@ -430,17 +491,20 @@ int main (int argc, char *argv[])
} }
arg->rig = my_rig; arg->rig = my_rig;
arg->clilen = sizeof(arg->cli_addr); arg->clilen = sizeof (arg->cli_addr);
arg->sock = accept(sock_listen, (struct sockaddr *)&arg->cli_addr, arg->sock = accept(sock_listen, (struct sockaddr *)&arg->cli_addr, &arg->clilen);
&arg->clilen);
if (arg->sock < 0) { if (arg->sock < 0) {
rig_debug(RIG_DEBUG_ERR, "accept: %s\n", strerror(errno)); handle_error (RIG_DEBUG_ERR, "accept");
break; break;
} }
rig_debug(RIG_DEBUG_VERBOSE, "Connection opened from %s:%d\n", if ((retcode = getnameinfo ((struct sockaddr const *)&arg->cli_addr, arg->clilen, host, sizeof (host)
inet_ntoa(arg->cli_addr.sin_addr), , serv, sizeof (serv), NI_NOFQDN)) < 0)
ntohs(arg->cli_addr.sin_port)); {
rig_debug (RIG_DEBUG_WARN, "Peer lookup error: %s", gai_strerror (retcode));
}
rig_debug(RIG_DEBUG_VERBOSE, "Connection opened from %s:%s\n",
host, serv);
#ifdef HAVE_PTHREAD #ifdef HAVE_PTHREAD
pthread_attr_init(&attr); pthread_attr_init(&attr);
@ -477,6 +541,8 @@ void * handle_socket(void *arg)
FILE *fsockin; FILE *fsockin;
FILE *fsockout; FILE *fsockout;
int retcode; int retcode;
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
#ifdef __MINGW32__ #ifdef __MINGW32__
int sock_osfhandle = _open_osfhandle(handle_data_arg->sock, _O_RDONLY); int sock_osfhandle = _open_osfhandle(handle_data_arg->sock, _O_RDONLY);
@ -513,9 +579,14 @@ void * handle_socket(void *arg)
} }
while (retcode == 0 || retcode == 2); while (retcode == 0 || retcode == 2);
rig_debug(RIG_DEBUG_VERBOSE, "Connection closed from %s:%d\n", if ((retcode = getnameinfo ((struct sockaddr const *)&handle_data_arg->cli_addr
inet_ntoa(handle_data_arg->cli_addr.sin_addr), , handle_data_arg->clilen, host, sizeof (host)
ntohs(handle_data_arg->cli_addr.sin_port)); , serv, sizeof (serv), NI_NOFQDN)) < 0)
{
rig_debug (RIG_DEBUG_WARN, "Peer lookup error: %s", gai_strerror (retcode));
}
rig_debug(RIG_DEBUG_VERBOSE, "Connection closed from %s:%s\n",
host, serv);
fclose(fsockin); fclose(fsockin);
fclose(fsockout); fclose(fsockout);