Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Support IPv6 sockets

Add support for IPv6 sockets. By default, the protocol family PF_UNSPEC
is used. Which family is chosen (PF_INET or PF_INET6) depends on the
configuration of the OS (for example, on Ubuntu, this can be set in
/etc/gai.conf). The first protocol family that returns a socket is used.

To explicitly use a a family, pass the {family, Family} option to open/2.
For IPv4, the option is inet; for IPv6, use inet6.
  • Loading branch information...
commit 721f6a5548bdc9ce60e0c2e0a01a3a0e1d177419 1 parent 8c211b0
@msantos authored
View
4 README.md
@@ -74,7 +74,7 @@ capabilities:
Erlang R13B03 (erts-5.7.4) [source] [rq:1] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
- 1> {ok, FD} = procket:open(53, [{progname, "sudo priv/procket"},{protocol, udp},{type, dgram}]).
+ 1> {ok, FD} = procket:open(53, [{protocol, udp},{type, dgram},{family, inet}]).
{ok,9}
2> {ok, S} = gen_udp:open(53, [{fd,FD}]).
{ok,#Port<0.929>}
@@ -96,7 +96,7 @@ To build the examples:
### Simple echo server
$ erl -pa ebin
- 1> echo:start(53, [{progname, "sudo priv/procket"}, {protocol, tcp}]).
+ 1> echo:start(53, [{protocol, tcp}, {type, stream}, {family, inet6}]).
### ICMP ping
View
6 c_src/procket.h
@@ -40,6 +40,7 @@
#include <unistd.h>
#include <sys/types.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <sys/un.h>
@@ -55,7 +56,7 @@
#include <ctype.h>
-#define PROCKET_VERSION "0.03"
+#define PROCKET_VERSION "0.04"
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
#endif
@@ -68,12 +69,11 @@ extern char *__progname;
typedef struct {
int fdtype; /* fd type requested */
char *path; /* path to pipe file */
- char *address; /* <port> or <ipaddr:port> */
+ char *address; /* IP address */
char *ifname; /* network interface name */
int verbose; /* Debug messages */
char *dev; /* Open a character device */
- in_addr_t ip; /* IP Address */
in_port_t port; /* Port */
int s; /* socket fd */
View
146 c_src/procket_cmd.c
@@ -37,9 +37,10 @@
int procket_open_fd(PROCKET_STATE *ps);
int procket_check_devname(char *dev, size_t len);
-int procket_parse_address(PROCKET_STATE *ps);
int procket_pipe(PROCKET_STATE *ps);
int procket_open_socket(PROCKET_STATE *ps);
+int procket_create_socket(PROCKET_STATE *ps, struct addrinfo *rp);
+int procket_bind_socket(PROCKET_STATE *ps, struct addrinfo *rp);
int procket_open_dev(PROCKET_STATE *ps);
int procket_open_char_dev(char *dev);
void error_result(PROCKET_STATE *ps, int err);
@@ -63,16 +64,15 @@ main(int argc, char *argv[])
if (ps == NULL)
error_result(ps, errno);
- ps->ip = INADDR_ANY;
ps->backlog = BACKLOG;
- ps->family = PF_INET;
+ ps->family = PF_UNSPEC; /* bind IPv4 and IPv6 socket */
ps->type = SOCK_STREAM;
ps->protocol = IPPROTO_TCP;
ps->fdtype = PROCKET_FD_SOCKET;
- while ( (ch = getopt(argc, argv, "b:d:F:hp:P:T:vI:")) != -1) {
+ while ( (ch = getopt(argc, argv, "b:d:F:hI:p:P:T:u:v")) != -1) {
switch (ch) {
case 'b': /* listen backlog */
ps->backlog = atoi(optarg);
@@ -80,7 +80,7 @@ main(int argc, char *argv[])
case 'F': /* socket family/domain */
ps->family = atoi(optarg);
break;
- case 'p': /* path to pipe */
+ case 'u': /* path to Unix socket */
ps->path = strdup(optarg);
if (ps->path == NULL)
@@ -89,6 +89,9 @@ main(int argc, char *argv[])
if (strlen(ps->path) >= UNIX_PATH_MAX)
usage(ps);
break;
+ case 'p': /* port */
+ ps->port = atoi(optarg);
+ break;
case 'P': /* socket protocol */
ps->protocol = atoi(optarg);
break;
@@ -133,6 +136,7 @@ main(int argc, char *argv[])
if (ps->address == NULL)
error_result(ps, errno);
+
}
if (procket_open_fd(ps) < 0)
@@ -153,22 +157,14 @@ procket_open_fd(PROCKET_STATE *ps)
{
switch (ps->fdtype) {
case PROCKET_FD_SOCKET:
- if (ps->protocol == IPPROTO_TCP || ps->protocol == IPPROTO_UDP) {
- if (procket_parse_address(ps) < 0)
- return -1;
- }
-
- if (procket_open_socket(ps) != 0)
- return -1;
- break;
+ return procket_open_socket(ps);
case PROCKET_FD_CHARDEV:
- if (procket_open_dev(ps) < 0)
- return -1;
- break;
- }
+ return procket_open_dev(ps);
- return 0;
+ default:
+ return -1;
+ }
}
@@ -177,6 +173,7 @@ procket_check_devname(char *dev, size_t len)
{
char *p = NULL;
+
if (strlen(dev) >= len)
return -1;
@@ -190,46 +187,85 @@ procket_check_devname(char *dev, size_t len)
int
-procket_parse_address(PROCKET_STATE *ps)
+procket_open_socket(PROCKET_STATE *ps)
{
- struct in_addr in;
- char *p = NULL;
+ struct addrinfo hints = {0};
+ struct addrinfo *res = NULL;
+ struct addrinfo *rp = NULL;
+ char port[10] = {0};
+ int err = 0;
+
+ hints.ai_family = ps->family;
+ hints.ai_socktype = ps->type;
+ hints.ai_protocol = ps->protocol;
+ hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE | AI_V4MAPPED | AI_ADDRCONFIG;
- if (ps->address == NULL) {
- errno = EINVAL;
+ (void)snprintf(port, sizeof(port), "%u", ps->port);
+
+ errno = EINVAL;
+ err = getaddrinfo(ps->address, port, &hints, &res);
+
+ if (err < 0)
return -1;
- }
- if ( (p = strchr(ps->address, ':')) == NULL) {
- ps->port = (in_port_t)atoi(ps->address);
- return 0;
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+ if (procket_create_socket(ps, rp) == 0)
+ break;
}
- *p++ = '\0';
- ps->port = (in_port_t)atoi(p);
- if (inet_aton(ps->address, &in) == 0) {
- errno = EINVAL;
+ freeaddrinfo(res);
+
+ if (ps->s < 0)
return -1;
- }
- ps->ip = in.s_addr;
return 0;
}
int
-procket_open_socket(PROCKET_STATE *ps)
+procket_create_socket(PROCKET_STATE *ps, struct addrinfo *rp)
{
- struct sockaddr_in sa = {0};
int flags = 0;
+ int err = 0;
- if ( (ps->s = socket(ps->family, ps->type, ps->protocol)) < 0)
- return -1;
+ ps->s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if (ps->s < 0)
+ return -1;
+
+ flags = fcntl(ps->s, F_GETFL, 0);
+
+ if (flags < 0)
+ goto ERR;
+ if (fcntl(ps->s, F_SETFL, flags|O_NONBLOCK) < 0)
+ goto ERR;
+
+ if (procket_bind_socket(ps, rp) < 0)
+ goto ERR;
+
+ return 0;
+
+ERR:
+ err = errno;
+
+ if (ps->s > -1) {
+ (void)close(ps->s);
+ errno = err;
+ ps->s = -1;
+ }
+
+ return -1;
+}
+
+
+ int
+procket_bind_socket(PROCKET_STATE *ps, struct addrinfo *rp)
+{
#ifdef SO_BINDTODEVICE
- if(ps->ifname) {
+ if (ps->ifname) {
struct ifreq ifr;
(void)snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ps->ifname);
@@ -238,25 +274,19 @@ procket_open_socket(PROCKET_STATE *ps)
}
#endif
- flags = fcntl(ps->s, F_GETFL, 0);
-
- if (flags < 0)
- return -1;
+ /* Erlang requires a bound socket */
+ switch (rp->ai_protocol) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ break;
+ default:
+ return 0;
+ }
- if (fcntl(ps->s, F_SETFL, flags|O_NONBLOCK) < 0)
+ if (bind(ps->s, rp->ai_addr, rp->ai_addrlen) < 0)
return -1;
- /* Erlang assumes the socket has already been bound */
- if ( (ps->protocol == IPPROTO_TCP) || (ps->protocol == IPPROTO_UDP)) {
- sa.sin_family = ps->family;
- sa.sin_port = htons(ps->port);
- sa.sin_addr.s_addr = ps->ip;
-
- if (bind(ps->s, (struct sockaddr *)&sa, sizeof(sa)) < 0)
- return -1;
- }
-
- return (0);
+ return 0;
}
@@ -286,6 +316,7 @@ procket_pipe(PROCKET_STATE *ps)
return 0;
}
+
/* character device support */
int
procket_open_dev(PROCKET_STATE *ps)
@@ -380,9 +411,10 @@ usage(PROCKET_STATE *ps)
{
(void)fprintf(stderr, "%s, %s\n", __progname, PROCKET_VERSION);
(void)fprintf(stderr,
- "usage: %s <options> <port|ipaddress:port>\n"
- " -p <path> path to Unix socket\n"
- " -F <family> family [default: PF_INET]\n"
+ "usage: %s <options> ipaddress>\n"
+ " -u <path> path to Unix socket\n"
+ " -p <port> port\n"
+ " -F <family> family [default: PF_UNSPEC]\n"
" -P <protocol> protocol [default: IPPROTO_TCP]\n"
" -T <type> type [default: SOCK_STREAM]\n"
#ifdef SO_BINDTODEVICE
View
14 src/procket.erl
@@ -201,18 +201,13 @@ fdget(Socket) ->
fdrecv(S).
make_args(Port, Options) ->
- Bind = " " ++ case proplists:lookup(ip, Options) of
- none ->
- integer_to_list(Port);
- IP ->
- get_switch(IP) ++ ":" ++ integer_to_list(Port)
- end,
proplists:get_value(progname, Options, "sudo " ++ progname()) ++ " " ++
- string:join([ get_switch(Arg) || Arg <- Options, element(1,Arg) /= ip ], " ") ++ Bind ++
+ string:join([ get_switch(Arg) || Arg <- Options, element(1,Arg) /= ip ], " ") ++
+ " " ++ get_switch({port, Port}) ++ get_switch(ip) ++
" > /dev/null 2>&1; printf $?".
get_switch({pipe, Arg}) ->
- "-p " ++ Arg;
+ "-u " ++ Arg;
get_switch({protocol, Proto}) when is_atom(Proto) ->
get_switch({protocol, protocol(Proto)});
@@ -232,6 +227,9 @@ get_switch({family, Family}) when is_integer(Family) ->
get_switch({ip, Arg}) when is_tuple(Arg) -> inet_parse:ntoa(Arg);
get_switch({ip, Arg}) when is_list(Arg) -> Arg;
+get_switch({port, Port}) when is_integer(Port) ->
+ "-p " ++ integer_to_list(Port);
+
get_switch({interface, Name}) when is_list(Name) ->
case is_interface(Name) of
true ->
Please sign in to comment.
Something went wrong with that request. Please try again.