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;
|
||||
#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
|
||||
*
|
||||
|
@ -87,9 +117,9 @@ int network_open(hamlib_port_t *rp, int default_port)
|
|||
{
|
||||
int fd; /* File descriptor for the port */
|
||||
int status;
|
||||
struct addrinfo hints, *res;
|
||||
char *portstr;
|
||||
char hostname[FILPATHLEN] = "127.0.0.1";
|
||||
struct addrinfo hints, *res, *saved_res;
|
||||
char *hoststr, *portstr, *bracketstr1, *bracketstr2;
|
||||
char hostname[FILPATHLEN];
|
||||
char defaultportstr[8];
|
||||
|
||||
#ifdef __MINGW32__
|
||||
|
@ -110,48 +140,90 @@ int network_open(hamlib_port_t *rp, int default_port)
|
|||
else
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (rp->pathname[0] == ':') {
|
||||
portstr = rp->pathname+1;
|
||||
} else {
|
||||
strncpy(hostname, rp->pathname, FILPATHLEN-1);
|
||||
|
||||
/* search last ':', because IPv6 may have some */
|
||||
portstr = strrchr(hostname, ':');
|
||||
if (portstr) {
|
||||
*portstr++ = '\0';
|
||||
} else {
|
||||
sprintf(defaultportstr, "%d", default_port);
|
||||
portstr = defaultportstr;
|
||||
hoststr = NULL; /* default of all local interfaces */
|
||||
if (rp->pathname && rp->pathname[0] == ':')
|
||||
{
|
||||
portstr = rp->pathname + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen (rp->pathname))
|
||||
{
|
||||
strncpy(hostname, rp->pathname, FILPATHLEN-1);
|
||||
hoststr = hostname;
|
||||
/* 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) {
|
||||
rig_debug(RIG_DEBUG_ERR, "Cannot get host \"%s\": %s\n",
|
||||
rp->pathname, gai_strerror(errno));
|
||||
return -RIG_ECONF;
|
||||
}
|
||||
saved_res = res;
|
||||
|
||||
/* we don't want a signal when connection get broken */
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (fd < 0)
|
||||
return -RIG_EIO;
|
||||
do
|
||||
{
|
||||
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);
|
||||
freeaddrinfo(res);
|
||||
if (status < 0) {
|
||||
rig_debug(RIG_DEBUG_ERR, "Cannot open NET device \"%s\": %s\n",
|
||||
rp->pathname, strerror(errno));
|
||||
close(fd);
|
||||
if ((status = connect(fd, res->ai_addr, res->ai_addrlen)) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
handle_error (RIG_DEBUG_WARN, "connect (trying next interface)");
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
rp->fd = fd;
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
|
|
135
tests/rigctld.c
135
tests/rigctld.c
|
@ -102,7 +102,7 @@ static struct option long_options[] =
|
|||
struct handle_data {
|
||||
RIG *rig;
|
||||
int sock;
|
||||
struct sockaddr_in cli_addr;
|
||||
struct sockaddr_storage cli_addr;
|
||||
socklen_t clilen;
|
||||
};
|
||||
|
||||
|
@ -120,6 +120,36 @@ const char *src_addr = NULL; /* INADDR_ANY */
|
|||
|
||||
#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[])
|
||||
{
|
||||
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 conf_parms[MAXCONFLEN] = "";
|
||||
|
||||
struct addrinfo hints, *result;
|
||||
int sockopt;
|
||||
struct addrinfo hints, *result, *saved_result;
|
||||
int sock_listen;
|
||||
int reuseaddr = 1;
|
||||
char host[NI_MAXHOST];
|
||||
char serv[NI_MAXSERV];
|
||||
|
||||
while(1) {
|
||||
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, "Report bugs to "
|
||||
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
||||
|
||||
my_rig = rig_init(my_model);
|
||||
my_rig = rig_init(my_model);
|
||||
|
||||
if (!my_rig) {
|
||||
fprintf(stderr, "Unknown rig num %d, or initialization error.\n",
|
||||
|
@ -370,7 +403,7 @@ int main (int argc, char *argv[])
|
|||
exit(1);
|
||||
}
|
||||
|
||||
int sockopt = SO_SYNCHRONOUS_NONALERT;
|
||||
sockopt = SO_SYNCHRONOUS_NONALERT;
|
||||
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sockopt, sizeof(sockopt));
|
||||
#endif
|
||||
|
||||
|
@ -388,28 +421,56 @@ int main (int argc, char *argv[])
|
|||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retcode));
|
||||
exit(2);
|
||||
}
|
||||
saved_result = result;
|
||||
|
||||
sock_listen = socket(result->ai_family, result->ai_socktype,
|
||||
result->ai_protocol);
|
||||
if (sock_listen < 0) {
|
||||
perror("ERROR opening socket");
|
||||
exit(2);
|
||||
}
|
||||
do
|
||||
{
|
||||
sock_listen = socket(result->ai_family, result->ai_socktype,
|
||||
result->ai_protocol);
|
||||
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,
|
||||
(char *)&reuseaddr, sizeof(reuseaddr)) < 0) {
|
||||
rig_debug(RIG_DEBUG_ERR, "setsockopt: %s\n", strerror(errno));
|
||||
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);
|
||||
}
|
||||
if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *)&reuseaddr, sizeof(reuseaddr)) < 0) {
|
||||
handle_error (RIG_DEBUG_ERR, "setsockopt");
|
||||
freeaddrinfo(saved_result); /* No longer needed */
|
||||
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) {
|
||||
rig_debug(RIG_DEBUG_ERR, "listening: %s\n", strerror(errno));
|
||||
handle_error (RIG_DEBUG_ERR, "listening");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
@ -430,17 +491,20 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
|
||||
arg->rig = my_rig;
|
||||
arg->clilen = sizeof(arg->cli_addr);
|
||||
arg->sock = accept(sock_listen, (struct sockaddr *)&arg->cli_addr,
|
||||
&arg->clilen);
|
||||
arg->clilen = sizeof (arg->cli_addr);
|
||||
arg->sock = accept(sock_listen, (struct sockaddr *)&arg->cli_addr, &arg->clilen);
|
||||
if (arg->sock < 0) {
|
||||
rig_debug(RIG_DEBUG_ERR, "accept: %s\n", strerror(errno));
|
||||
handle_error (RIG_DEBUG_ERR, "accept");
|
||||
break;
|
||||
}
|
||||
|
||||
rig_debug(RIG_DEBUG_VERBOSE, "Connection opened from %s:%d\n",
|
||||
inet_ntoa(arg->cli_addr.sin_addr),
|
||||
ntohs(arg->cli_addr.sin_port));
|
||||
if ((retcode = getnameinfo ((struct sockaddr const *)&arg->cli_addr, arg->clilen, host, sizeof (host)
|
||||
, serv, sizeof (serv), NI_NOFQDN)) < 0)
|
||||
{
|
||||
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
|
||||
pthread_attr_init(&attr);
|
||||
|
@ -477,6 +541,8 @@ void * handle_socket(void *arg)
|
|||
FILE *fsockin;
|
||||
FILE *fsockout;
|
||||
int retcode;
|
||||
char host[NI_MAXHOST];
|
||||
char serv[NI_MAXSERV];
|
||||
|
||||
#ifdef __MINGW32__
|
||||
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);
|
||||
|
||||
rig_debug(RIG_DEBUG_VERBOSE, "Connection closed from %s:%d\n",
|
||||
inet_ntoa(handle_data_arg->cli_addr.sin_addr),
|
||||
ntohs(handle_data_arg->cli_addr.sin_port));
|
||||
if ((retcode = getnameinfo ((struct sockaddr const *)&handle_data_arg->cli_addr
|
||||
, handle_data_arg->clilen, host, sizeof (host)
|
||||
, 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(fsockout);
|
||||
|
|
Ładowanie…
Reference in New Issue