kopia lustrzana https://github.com/Hamlib/Hamlib
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
rodzic
a4a93ac044
commit
181a3e9697
126
src/network.c
126
src/network.c
|
@ -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 {
|
{
|
||||||
strncpy(hostname, rp->pathname, FILPATHLEN-1);
|
portstr = rp->pathname + 1;
|
||||||
|
}
|
||||||
/* search last ':', because IPv6 may have some */
|
else
|
||||||
portstr = strrchr(hostname, ':');
|
{
|
||||||
if (portstr) {
|
if (strlen (rp->pathname))
|
||||||
*portstr++ = '\0';
|
{
|
||||||
} else {
|
strncpy(hostname, rp->pathname, FILPATHLEN-1);
|
||||||
sprintf(defaultportstr, "%d", default_port);
|
hoststr = hostname;
|
||||||
portstr = defaultportstr;
|
/* look for IPv6 numeric form [<addr>] */
|
||||||
|
bracketstr1 = strchr(hoststr, '[');
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(defaultportstr, "%d", default_port);
|
||||||
|
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
|
||||||
|
|
||||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
do
|
||||||
if (fd < 0)
|
{
|
||||||
return -RIG_EIO;
|
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
handle_error (RIG_DEBUG_ERR, "socket");
|
||||||
|
freeaddrinfo (saved_res);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
135
tests/rigctld.c
135
tests/rigctld.c
|
@ -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;
|
||||||
|
@ -283,13 +316,13 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rig_set_debug(verbose);
|
rig_set_debug(verbose);
|
||||||
|
|
||||||
rig_debug(RIG_DEBUG_VERBOSE, "rigctld, %s\n", hamlib_version);
|
rig_debug(RIG_DEBUG_VERBOSE, "rigctld, %s\n", hamlib_version);
|
||||||
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
|
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
|
||||||
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
||||||
|
|
||||||
my_rig = rig_init(my_model);
|
my_rig = rig_init(my_model);
|
||||||
|
|
||||||
if (!my_rig) {
|
if (!my_rig) {
|
||||||
fprintf(stderr, "Unknown rig num %d, or initialization error.\n",
|
fprintf(stderr, "Unknown rig num %d, or initialization error.\n",
|
||||||
|
@ -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;
|
||||||
|
|
||||||
sock_listen = socket(result->ai_family, result->ai_socktype,
|
do
|
||||||
result->ai_protocol);
|
{
|
||||||
if (sock_listen < 0) {
|
sock_listen = socket(result->ai_family, result->ai_socktype,
|
||||||
perror("ERROR opening socket");
|
result->ai_protocol);
|
||||||
exit(2);
|
if (sock_listen < 0) {
|
||||||
}
|
handle_error (RIG_DEBUG_ERR, "socket");
|
||||||
|
freeaddrinfo(saved_result); /* No longer needed */
|
||||||
|
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 */
|
||||||
}
|
exit (1);
|
||||||
if (bind(sock_listen, result->ai_addr, result->ai_addrlen) < 0) {
|
}
|
||||||
rig_debug(RIG_DEBUG_ERR, "binding: %s\n", strerror(errno));
|
|
||||||
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);
|
||||||
|
|
Ładowanie…
Reference in New Issue