Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
Conflicts: NEWS README configure.in lib/utils.c src/linux/device.c src/meta.c src/net.h src/net_setup.c src/net_socket.c src/protocol.c src/protocol_auth.c src/tincd.c
This commit is contained in:
commit
19be9cf715
16 changed files with 517 additions and 52 deletions
11
NEWS
11
NEWS
|
@ -28,6 +28,17 @@ Version 1.1pre1 June 25 2011
|
||||||
Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
|
Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
|
||||||
version of tinc.
|
version of tinc.
|
||||||
|
|
||||||
|
Version 1.0.19 June 25 2012
|
||||||
|
|
||||||
|
* Allow :: notation in IPv6 Subnets.
|
||||||
|
|
||||||
|
* Add support for systemd style socket activation.
|
||||||
|
|
||||||
|
* Allow environment variables to be used for the Name option.
|
||||||
|
|
||||||
|
* Add basic support for SOCKS proxies, HTTP proxies, and proxying through an
|
||||||
|
external command.
|
||||||
|
|
||||||
Version 1.0.18 March 25 2012
|
Version 1.0.18 March 25 2012
|
||||||
|
|
||||||
* Fixed IPv6 in switch mode by turning off DecrementTTL by default.
|
* Fixed IPv6 in switch mode by turning off DecrementTTL by default.
|
||||||
|
|
1
THANKS
1
THANKS
|
@ -3,6 +3,7 @@ We would like to thank the following people for their contributions to tinc:
|
||||||
* Alexander Reil and Gemeinde Berg
|
* Alexander Reil and Gemeinde Berg
|
||||||
* Allesandro Gatti
|
* Allesandro Gatti
|
||||||
* Andreas van Cranenburgh
|
* Andreas van Cranenburgh
|
||||||
|
* Anthony G. Basile
|
||||||
* Armijn Hemel
|
* Armijn Hemel
|
||||||
* Brandon Black
|
* Brandon Black
|
||||||
* Cris van Pelt
|
* Cris van Pelt
|
||||||
|
|
|
@ -159,8 +159,25 @@ It is possible to bind only to a single interface with this variable.
|
||||||
.Pp
|
.Pp
|
||||||
This option may not work on all platforms.
|
This option may not work on all platforms.
|
||||||
|
|
||||||
.It Va Broadcast Li = yes | no Po yes Pc Bq experimental
|
.It Va Broadcast Li = no | mst | direct Po mst Pc Bq experimental
|
||||||
When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode.
|
This option selects the way broadcast packets are sent to other daemons.
|
||||||
|
NOTE: all nodes in a VPN must use the same
|
||||||
|
.Va Broadcast
|
||||||
|
mode, otherwise routing loops can form.
|
||||||
|
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It no
|
||||||
|
Broadcast packets are never sent to other nodes.
|
||||||
|
|
||||||
|
.It mst
|
||||||
|
Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree.
|
||||||
|
This ensures broadcast packets reach all nodes.
|
||||||
|
|
||||||
|
.It direct
|
||||||
|
Broadcast packets are sent directly to all nodes that can be reached directly.
|
||||||
|
Broadcast packets received from other nodes are never forwarded.
|
||||||
|
If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
|
||||||
|
.El
|
||||||
|
|
||||||
.It Va ConnectTo Li = Ar name
|
.It Va ConnectTo Li = Ar name
|
||||||
Specifies which other tinc daemon to connect to on startup.
|
Specifies which other tinc daemon to connect to on startup.
|
||||||
|
@ -409,6 +426,19 @@ while no routing table is managed.
|
||||||
.It Va Name Li = Ar name Bq required
|
.It Va Name Li = Ar name Bq required
|
||||||
This is the name which identifies this tinc daemon.
|
This is the name which identifies this tinc daemon.
|
||||||
It must be unique for the virtual private network this daemon will connect to.
|
It must be unique for the virtual private network this daemon will connect to.
|
||||||
|
The Name may only consist of alphanumeric and underscore characters.
|
||||||
|
|
||||||
|
If
|
||||||
|
.Va Name
|
||||||
|
starts with a
|
||||||
|
.Li $ ,
|
||||||
|
then the contents of the environment variable that follows will be used.
|
||||||
|
In that case, invalid characters will be converted to underscores.
|
||||||
|
If
|
||||||
|
.Va Name
|
||||||
|
is
|
||||||
|
.Li $HOST ,
|
||||||
|
but no such environment variable exist, the hostname will be read using the gethostnname() system call.
|
||||||
|
|
||||||
.It Va PingInterval Li = Ar seconds Pq 60
|
.It Va PingInterval Li = Ar seconds Pq 60
|
||||||
The number of seconds of inactivity that
|
The number of seconds of inactivity that
|
||||||
|
@ -441,8 +471,41 @@ specified in the configuration file.
|
||||||
When this option is used the priority of the tincd process will be adjusted.
|
When this option is used the priority of the tincd process will be adjusted.
|
||||||
Increasing the priority may help to reduce latency and packet loss on the VPN.
|
Increasing the priority may help to reduce latency and packet loss on the VPN.
|
||||||
|
|
||||||
|
.It Va Proxy Li = socks4 | socks5 | http | exec Ar ... Bq experimental
|
||||||
|
Use a proxy when making outgoing connections.
|
||||||
|
The following proxy types are currently supported:
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It socks4 Ar address Ar port Op Ar username
|
||||||
|
Connects to the proxy using the SOCKS version 4 protocol.
|
||||||
|
Optionally, a
|
||||||
|
.Ar username
|
||||||
|
can be supplied which will be passed on to the proxy server.
|
||||||
|
Only IPv4 connections can be proxied using SOCKS 4.
|
||||||
|
.It socks5 Ar address Ar port Op Ar username Ar password
|
||||||
|
Connect to the proxy using the SOCKS version 5 protocol.
|
||||||
|
If a
|
||||||
|
.Ar username
|
||||||
|
and
|
||||||
|
.Ar password
|
||||||
|
are given, basic username/password authentication will be used,
|
||||||
|
otherwise no authentication will be used.
|
||||||
|
.It http Ar address Ar port
|
||||||
|
Connects to the proxy and sends a HTTP CONNECT request.
|
||||||
|
.It exec Ar command
|
||||||
|
Executes the given
|
||||||
|
.Ar command
|
||||||
|
which should set up the outgoing connection.
|
||||||
|
The environment variables
|
||||||
|
.Ev NAME ,
|
||||||
|
.Ev NODE ,
|
||||||
|
.Ev REMOTEADDRES
|
||||||
|
and
|
||||||
|
.Ev REMOTEPORT
|
||||||
|
are available.
|
||||||
|
.El
|
||||||
|
|
||||||
.It Va ReplayWindow Li = Ar bytes Pq 16
|
.It Va ReplayWindow Li = Ar bytes Pq 16
|
||||||
This is the size of the replay tracking window for each remote node, in bytes.
|
vhis is the size of the replay tracking window for each remote node, in bytes.
|
||||||
The window is a bitfield which tracks 1 packet per bit, so for example
|
The window is a bitfield which tracks 1 packet per bit, so for example
|
||||||
the default setting of 16 will track up to 128 packets in the window. In high
|
the default setting of 16 will track up to 128 packets in the window. In high
|
||||||
bandwidth scenarios, setting this to a higher value can reduce packet loss from
|
bandwidth scenarios, setting this to a higher value can reduce packet loss from
|
||||||
|
|
|
@ -801,8 +801,23 @@ variable.
|
||||||
This option may not work on all platforms.
|
This option may not work on all platforms.
|
||||||
|
|
||||||
@cindex Broadcast
|
@cindex Broadcast
|
||||||
@item Broadcast = <yes | no> (yes) [experimental]
|
@item Broadcast = <no | mst | direct> (mst) [experimental]
|
||||||
When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode.
|
This option selects the way broadcast packets are sent to other daemons.
|
||||||
|
@emph{NOTE: all nodes in a VPN must use the same Broadcast mode, otherwise routing loops can form.}
|
||||||
|
|
||||||
|
@table @asis
|
||||||
|
@item no
|
||||||
|
Broadcast packets are never sent to other nodes.
|
||||||
|
|
||||||
|
@item mst
|
||||||
|
Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree.
|
||||||
|
This ensures broadcast packets reach all nodes.
|
||||||
|
|
||||||
|
@item direct
|
||||||
|
Broadcast packets are sent directly to all nodes that can be reached directly.
|
||||||
|
Broadcast packets received from other nodes are never forwarded.
|
||||||
|
If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
|
||||||
|
@end table
|
||||||
|
|
||||||
@cindex ConnectTo
|
@cindex ConnectTo
|
||||||
@item ConnectTo = <@var{name}>
|
@item ConnectTo = <@var{name}>
|
||||||
|
@ -1031,6 +1046,11 @@ This only has effect when Mode is set to "switch".
|
||||||
This is a symbolic name for this connection.
|
This is a symbolic name for this connection.
|
||||||
The name should consist only of alfanumeric and underscore characters (a-z, A-Z, 0-9 and _).
|
The name should consist only of alfanumeric and underscore characters (a-z, A-Z, 0-9 and _).
|
||||||
|
|
||||||
|
If Name starts with a $, then the contents of the environment variable that follows will be used.
|
||||||
|
In that case, invalid characters will be converted to underscores.
|
||||||
|
If Name is $HOST, but no such environment variable exist,
|
||||||
|
the hostname will be read using the gethostnname() system call.
|
||||||
|
|
||||||
@cindex PingInterval
|
@cindex PingInterval
|
||||||
@item PingInterval = <@var{seconds}> (60)
|
@item PingInterval = <@var{seconds}> (60)
|
||||||
The number of seconds of inactivity that tinc will wait before sending a
|
The number of seconds of inactivity that tinc will wait before sending a
|
||||||
|
@ -1068,6 +1088,33 @@ specified in the configuration file.
|
||||||
When this option is used the priority of the tincd process will be adjusted.
|
When this option is used the priority of the tincd process will be adjusted.
|
||||||
Increasing the priority may help to reduce latency and packet loss on the VPN.
|
Increasing the priority may help to reduce latency and packet loss on the VPN.
|
||||||
|
|
||||||
|
@cindex Proxy
|
||||||
|
@item Proxy = socks4 | socks4 | http | exec @var{...} [experimental]
|
||||||
|
Use a proxy when making outgoing connections.
|
||||||
|
The following proxy types are currently supported:
|
||||||
|
|
||||||
|
@table @asis
|
||||||
|
@cindex socks4
|
||||||
|
@item socks4 <@var{address}> <@var{port}> [<@var{username}>]
|
||||||
|
Connects to the proxy using the SOCKS version 4 protocol.
|
||||||
|
Optionally, a @var{username} can be supplied which will be passed on to the proxy server.
|
||||||
|
|
||||||
|
@cindex socks5
|
||||||
|
@item socks4 <@var{address}> <@var{port}> [<@var{username}> <@var{password}>]
|
||||||
|
Connect to the proxy using the SOCKS version 5 protocol.
|
||||||
|
If a @var{username} and @var{password} are given, basic username/password authentication will be used,
|
||||||
|
otherwise no authentication will be used.
|
||||||
|
|
||||||
|
@cindex http
|
||||||
|
@item http <@var{address}> <@var{port}>
|
||||||
|
Connects to the proxy and sends a HTTP CONNECT request.
|
||||||
|
|
||||||
|
@cindex exec
|
||||||
|
@item exec <@var{command}>
|
||||||
|
Executes the given command which should set up the outgoing connection.
|
||||||
|
The environment variables @env{NAME}, @env{NODE}, @env{REMOTEADDRES} and @env{REMOTEPORT} are available.
|
||||||
|
@end table
|
||||||
|
|
||||||
@cindex ReplayWindow
|
@cindex ReplayWindow
|
||||||
@item ReplayWindow = <bytes> (16)
|
@item ReplayWindow = <bytes> (16)
|
||||||
This is the size of the replay tracking window for each remote node, in bytes.
|
This is the size of the replay tracking window for each remote node, in bytes.
|
||||||
|
|
1
have.h
1
have.h
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
#ifdef HAVE_MINGW
|
#ifdef HAVE_MINGW
|
||||||
#include <w32api.h>
|
#include <w32api.h>
|
||||||
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
10
src/meta.c
10
src/meta.c
|
@ -196,7 +196,15 @@ bool receive_meta(connection_t *c) {
|
||||||
if(c->tcplen) {
|
if(c->tcplen) {
|
||||||
char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
|
char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
|
||||||
if(tcpbuffer) {
|
if(tcpbuffer) {
|
||||||
receive_tcppacket(c, tcpbuffer, c->tcplen);
|
if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
|
||||||
|
if(tcpbuffer[0] == 0 && tcpbuffer[1] == 0x5a) {
|
||||||
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
|
||||||
|
} else {
|
||||||
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
receive_tcppacket(c, tcpbuffer, c->tcplen);
|
||||||
c->tcplen = 0;
|
c->tcplen = 0;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
15
src/net.h
15
src/net.h
|
@ -121,6 +121,20 @@ extern char *myport;
|
||||||
extern int contradicting_add_edge;
|
extern int contradicting_add_edge;
|
||||||
extern int contradicting_del_edge;
|
extern int contradicting_del_edge;
|
||||||
|
|
||||||
|
extern char *proxyhost;
|
||||||
|
extern char *proxyport;
|
||||||
|
extern char *proxyuser;
|
||||||
|
extern char *proxypass;
|
||||||
|
typedef enum proxytype_t {
|
||||||
|
PROXY_NONE = 0,
|
||||||
|
PROXY_SOCKS4,
|
||||||
|
PROXY_SOCKS4A,
|
||||||
|
PROXY_SOCKS5,
|
||||||
|
PROXY_HTTP,
|
||||||
|
PROXY_EXEC,
|
||||||
|
} proxytype_t;
|
||||||
|
extern proxytype_t proxytype;
|
||||||
|
|
||||||
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
|
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
@ -135,6 +149,7 @@ extern int setup_vpn_in_socket(const sockaddr_t *);
|
||||||
extern void send_packet(struct node_t *, vpn_packet_t *);
|
extern void send_packet(struct node_t *, vpn_packet_t *);
|
||||||
extern void receive_tcppacket(struct connection_t *, const char *, int);
|
extern void receive_tcppacket(struct connection_t *, const char *, int);
|
||||||
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
|
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
|
||||||
|
extern char *get_name(void);
|
||||||
extern bool setup_network(void);
|
extern bool setup_network(void);
|
||||||
extern void setup_outgoing_connection(struct outgoing_t *);
|
extern void setup_outgoing_connection(struct outgoing_t *);
|
||||||
extern void try_outgoing_connections(void);
|
extern void try_outgoing_connections(void);
|
||||||
|
|
|
@ -576,24 +576,50 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
|
||||||
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
|
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
|
||||||
splay_node_t *node;
|
splay_node_t *node;
|
||||||
connection_t *c;
|
connection_t *c;
|
||||||
|
node_t *n;
|
||||||
|
|
||||||
|
// Always give ourself a copy of the packet.
|
||||||
|
if(from != myself)
|
||||||
|
send_packet(myself, packet);
|
||||||
|
|
||||||
|
// In TunnelServer mode, do not forward broadcast packets.
|
||||||
|
// The MST might not be valid and create loops.
|
||||||
|
if(tunnelserver || broadcast_mode == BMODE_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
|
logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
|
||||||
packet->len, from->name, from->hostname);
|
packet->len, from->name, from->hostname);
|
||||||
|
|
||||||
if(from != myself) {
|
switch(broadcast_mode) {
|
||||||
send_packet(myself, packet);
|
// In MST mode, broadcast packets travel via the Minimum Spanning Tree.
|
||||||
|
// This guarantees all nodes receive the broadcast packet, and
|
||||||
|
// usually distributes the sending of broadcast packets over all nodes.
|
||||||
|
case BMODE_MST:
|
||||||
|
for(node = connection_tree->head; node; node = node->next) {
|
||||||
|
c = node->data;
|
||||||
|
|
||||||
// In TunnelServer mode, do not forward broadcast packets.
|
if(c->status.active && c->status.mst && c != from->nexthop->connection)
|
||||||
// The MST might not be valid and create loops.
|
send_packet(c->node, packet);
|
||||||
if(tunnelserver)
|
}
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
for(node = connection_tree->head; node; node = node->next) {
|
// In direct mode, we send copies to each node we know of.
|
||||||
c = node->data;
|
// However, this only reaches nodes that can be reached in a single hop.
|
||||||
|
// We don't have enough information to forward broadcast packets in this case.
|
||||||
|
case BMODE_DIRECT:
|
||||||
|
if(from != myself)
|
||||||
|
break;
|
||||||
|
|
||||||
if(c->status.active && c->status.mst && c != from->nexthop->connection)
|
for(node = node_udp_tree->head; node; node = node->next) {
|
||||||
send_packet(c->node, packet);
|
n = node->data;
|
||||||
|
|
||||||
|
if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n))
|
||||||
|
send_packet(n, packet);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
src/net_setup.c
130
src/net_setup.c
|
@ -46,6 +46,12 @@ char *myport;
|
||||||
static struct event device_ev;
|
static struct event device_ev;
|
||||||
devops_t devops;
|
devops_t devops;
|
||||||
|
|
||||||
|
char *proxyhost;
|
||||||
|
char *proxyport;
|
||||||
|
char *proxyuser;
|
||||||
|
char *proxypass;
|
||||||
|
proxytype_t proxytype;
|
||||||
|
|
||||||
bool node_read_ecdsa_public_key(node_t *n) {
|
bool node_read_ecdsa_public_key(node_t *n) {
|
||||||
if(ecdsa_active(&n->ecdsa))
|
if(ecdsa_active(&n->ecdsa))
|
||||||
return true;
|
return true;
|
||||||
|
@ -340,6 +346,44 @@ void load_all_subnets(void) {
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *get_name(void) {
|
||||||
|
char *name = NULL;
|
||||||
|
|
||||||
|
get_config_string(lookup_config(config_tree, "Name"), &name);
|
||||||
|
|
||||||
|
if(!name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(*name == '$') {
|
||||||
|
char *envname = getenv(name + 1);
|
||||||
|
if(!envname) {
|
||||||
|
if(strcmp(name + 1, "HOST")) {
|
||||||
|
fprintf(stderr, "Invalid Name: environment variable %s does not exist\n", name + 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
envname = alloca(32);
|
||||||
|
if(gethostname(envname, 32)) {
|
||||||
|
fprintf(stderr, "Could not get hostname: %s\n", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
envname[31] = 0;
|
||||||
|
}
|
||||||
|
free(name);
|
||||||
|
name = xstrdup(envname);
|
||||||
|
for(char *c = name; *c; c++)
|
||||||
|
if(!isalnum(*c))
|
||||||
|
*c = '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!check_id(name)) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
|
||||||
|
free(name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Configure node_t myself and set up the local sockets (listen only)
|
Configure node_t myself and set up the local sockets (listen only)
|
||||||
*/
|
*/
|
||||||
|
@ -349,6 +393,8 @@ static bool setup_myself(void) {
|
||||||
char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
|
char *name, *hostname, *mode, *afname, *cipher, *digest, *type;
|
||||||
char *fname = NULL;
|
char *fname = NULL;
|
||||||
char *address = NULL;
|
char *address = NULL;
|
||||||
|
char *proxy = NULL;
|
||||||
|
char *space;
|
||||||
char *envp[5];
|
char *envp[5];
|
||||||
struct addrinfo *ai, *aip, hint = {0};
|
struct addrinfo *ai, *aip, hint = {0};
|
||||||
bool choice;
|
bool choice;
|
||||||
|
@ -365,17 +411,11 @@ static bool setup_myself(void) {
|
||||||
myself->connection->protocol_major = PROT_MAJOR;
|
myself->connection->protocol_major = PROT_MAJOR;
|
||||||
myself->connection->protocol_minor = PROT_MINOR;
|
myself->connection->protocol_minor = PROT_MINOR;
|
||||||
|
|
||||||
if(!get_config_string(lookup_config(config_tree, "Name"), &name)) { /* Not acceptable */
|
if(!(name = get_name())) {
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
|
logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!check_id(name)) {
|
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
|
|
||||||
free(name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
myself->name = name;
|
myself->name = name;
|
||||||
myself->connection->name = xstrdup(name);
|
myself->connection->name = xstrdup(name);
|
||||||
xasprintf(&fname, "%s/hosts/%s", confbase, name);
|
xasprintf(&fname, "%s/hosts/%s", confbase, name);
|
||||||
|
@ -404,6 +444,68 @@ static bool setup_myself(void) {
|
||||||
sockaddr2str(&sa, NULL, &myport);
|
sockaddr2str(&sa, NULL, &myport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
|
||||||
|
if(proxy) {
|
||||||
|
if((space = strchr(proxy, ' ')))
|
||||||
|
*space++ = 0;
|
||||||
|
|
||||||
|
if(!strcasecmp(proxy, "none")) {
|
||||||
|
proxytype = PROXY_NONE;
|
||||||
|
} else if(!strcasecmp(proxy, "socks4")) {
|
||||||
|
proxytype = PROXY_SOCKS4;
|
||||||
|
} else if(!strcasecmp(proxy, "socks4a")) {
|
||||||
|
proxytype = PROXY_SOCKS4A;
|
||||||
|
} else if(!strcasecmp(proxy, "socks5")) {
|
||||||
|
proxytype = PROXY_SOCKS5;
|
||||||
|
} else if(!strcasecmp(proxy, "http")) {
|
||||||
|
proxytype = PROXY_HTTP;
|
||||||
|
} else if(!strcasecmp(proxy, "exec")) {
|
||||||
|
proxytype = PROXY_EXEC;
|
||||||
|
} else {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(proxytype) {
|
||||||
|
case PROXY_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROXY_EXEC:
|
||||||
|
if(!space || !*space) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Argument expected for proxy type exec!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proxyhost = xstrdup(space);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROXY_SOCKS4:
|
||||||
|
case PROXY_SOCKS4A:
|
||||||
|
case PROXY_SOCKS5:
|
||||||
|
case PROXY_HTTP:
|
||||||
|
proxyhost = space;
|
||||||
|
if(space && (space = strchr(space, ' ')))
|
||||||
|
*space++ = 0, proxyport = space;
|
||||||
|
if(space && (space = strchr(space, ' ')))
|
||||||
|
*space++ = 0, proxyuser = space;
|
||||||
|
if(space && (space = strchr(space, ' ')))
|
||||||
|
*space++ = 0, proxypass = space;
|
||||||
|
if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proxyhost = xstrdup(proxyhost);
|
||||||
|
proxyport = xstrdup(proxyport);
|
||||||
|
if(proxyuser && *proxyuser)
|
||||||
|
proxyuser = xstrdup(proxyuser);
|
||||||
|
if(proxypass && *proxypass)
|
||||||
|
proxypass = xstrdup(proxypass);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
/* Read in all the subnets specified in the host configuration file */
|
/* Read in all the subnets specified in the host configuration file */
|
||||||
|
|
||||||
cfg = lookup_config(config_tree, "Subnet");
|
cfg = lookup_config(config_tree, "Subnet");
|
||||||
|
@ -474,7 +576,19 @@ static bool setup_myself(void) {
|
||||||
|
|
||||||
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
|
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
|
||||||
get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
|
get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
|
||||||
get_config_bool(lookup_config(config_tree, "Broadcast"), &broadcast);
|
if(get_config_string(lookup_config(config_tree, "Broadcast"), &mode)) {
|
||||||
|
if(!strcasecmp(mode, "no"))
|
||||||
|
broadcast_mode = BMODE_NONE;
|
||||||
|
else if(!strcasecmp(mode, "yes") || !strcasecmp(mode, "mst"))
|
||||||
|
broadcast_mode = BMODE_MST;
|
||||||
|
else if(!strcasecmp(mode, "direct"))
|
||||||
|
broadcast_mode = BMODE_DIRECT;
|
||||||
|
else {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(mode);
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(SOL_IP) || !defined(IP_TOS)
|
#if !defined(SOL_IP) || !defined(IP_TOS)
|
||||||
if(priorityinheritance)
|
if(priorityinheritance)
|
||||||
|
|
|
@ -292,7 +292,8 @@ void retry_outgoing(outgoing_t *outgoing) {
|
||||||
void finish_connecting(connection_t *c) {
|
void finish_connecting(connection_t *c) {
|
||||||
logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
|
logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
|
||||||
|
|
||||||
configure_tcp(c);
|
if(proxytype != PROXY_EXEC)
|
||||||
|
configure_tcp(c);
|
||||||
|
|
||||||
c->last_ping_time = time(NULL);
|
c->last_ping_time = time(NULL);
|
||||||
c->status.connecting = false;
|
c->status.connecting = false;
|
||||||
|
@ -300,8 +301,57 @@ void finish_connecting(connection_t *c) {
|
||||||
send_id(c);
|
send_id(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_outgoing_pipe(connection_t *c, char *command) {
|
||||||
|
#ifndef HAVE_MINGW
|
||||||
|
int fd[2];
|
||||||
|
|
||||||
|
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fork()) {
|
||||||
|
c->socket = fd[0];
|
||||||
|
close(fd[1]);
|
||||||
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(0);
|
||||||
|
close(1);
|
||||||
|
close(fd[0]);
|
||||||
|
dup2(fd[1], 0);
|
||||||
|
dup2(fd[1], 1);
|
||||||
|
close(fd[1]);
|
||||||
|
|
||||||
|
// Other filedescriptors should be closed automatically by CLOEXEC
|
||||||
|
|
||||||
|
char *host = NULL;
|
||||||
|
char *port = NULL;
|
||||||
|
|
||||||
|
sockaddr2str(&c->address, &host, &port);
|
||||||
|
setenv("REMOTEADDRESS", host, true);
|
||||||
|
setenv("REMOTEPORT", port, true);
|
||||||
|
setenv("NODE", c->name, true);
|
||||||
|
setenv("NAME", myself->name, true);
|
||||||
|
if(netname)
|
||||||
|
setenv("NETNAME", netname, true);
|
||||||
|
|
||||||
|
int result = system(command);
|
||||||
|
if(result < 0)
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
|
||||||
|
else if(result)
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
|
||||||
|
exit(result);
|
||||||
|
#else
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool do_outgoing_connection(connection_t *c) {
|
bool do_outgoing_connection(connection_t *c) {
|
||||||
char *address, *port, *space;
|
char *address, *port, *space;
|
||||||
|
struct addrinfo *proxyai = NULL;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if(!c->outgoing) {
|
if(!c->outgoing) {
|
||||||
|
@ -357,32 +407,48 @@ begin:
|
||||||
logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", c->name,
|
logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", c->name,
|
||||||
c->hostname);
|
c->hostname);
|
||||||
|
|
||||||
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
|
if(!proxytype) {
|
||||||
|
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||||
#ifdef FD_CLOEXEC
|
configure_tcp(c);
|
||||||
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
|
} else if(proxytype == PROXY_EXEC) {
|
||||||
#endif
|
do_outgoing_pipe(c, proxyhost);
|
||||||
|
} else {
|
||||||
|
proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
|
||||||
|
if(!proxyai)
|
||||||
|
goto begin;
|
||||||
|
logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
|
||||||
|
c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
if(c->socket == -1) {
|
if(c->socket == -1) {
|
||||||
logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
|
||||||
goto begin;
|
goto begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
|
#ifdef FD_CLOEXEC
|
||||||
int option = 1;
|
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
|
||||||
if(c->address.sa.sa_family == AF_INET6)
|
|
||||||
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bind_to_interface(c->socket);
|
if(proxytype != PROXY_EXEC) {
|
||||||
|
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
|
||||||
|
int option = 1;
|
||||||
|
if(c->address.sa.sa_family == AF_INET6)
|
||||||
|
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Optimize TCP settings */
|
bind_to_interface(c->socket);
|
||||||
|
}
|
||||||
configure_tcp(c);
|
|
||||||
|
|
||||||
/* Connect */
|
/* Connect */
|
||||||
|
|
||||||
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
|
if(!proxytype) {
|
||||||
|
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
|
||||||
|
} else if(proxytype == PROXY_EXEC) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
|
||||||
|
freeaddrinfo(proxyai);
|
||||||
|
}
|
||||||
|
|
||||||
if(result == -1) {
|
if(result == -1) {
|
||||||
if(sockinprogress(sockerrno)) {
|
if(sockinprogress(sockerrno)) {
|
||||||
|
|
|
@ -82,8 +82,10 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if(sa->sa.sa_family == AF_UNKNOWN) {
|
if(sa->sa.sa_family == AF_UNKNOWN) {
|
||||||
*addrstr = xstrdup(sa->unknown.address);
|
if(addrstr)
|
||||||
*portstr = xstrdup(sa->unknown.port);
|
*addrstr = xstrdup(sa->unknown.address);
|
||||||
|
if(portstr)
|
||||||
|
*portstr = xstrdup(sa->unknown.port);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,20 @@ void forward_request(connection_t *from, const char *request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool receive_request(connection_t *c, const char *request) {
|
bool receive_request(connection_t *c, const char *request) {
|
||||||
|
if(proxytype == PROXY_HTTP && c->allow_request == ID) {
|
||||||
|
if(!request[0] || request[0] == '\r')
|
||||||
|
return true;
|
||||||
|
if(!strncasecmp(request, "HTTP/1.1 ", 9)) {
|
||||||
|
if(!strncmp(request + 9, "200", 3)) {
|
||||||
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int reqno = atoi(request);
|
int reqno = atoi(request);
|
||||||
|
|
||||||
if(reqno || *request == '0') {
|
if(reqno || *request == '0') {
|
||||||
|
|
|
@ -42,6 +42,92 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
|
||||||
|
static bool send_proxyrequest(connection_t *c) {
|
||||||
|
switch(proxytype) {
|
||||||
|
case PROXY_HTTP: {
|
||||||
|
char *host;
|
||||||
|
char *port;
|
||||||
|
|
||||||
|
sockaddr2str(&c->address, &host, &port);
|
||||||
|
send_request(c, "CONNECT %s:%s HTTP/1.1\r\n\r", host, port);
|
||||||
|
free(host);
|
||||||
|
free(port);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case PROXY_SOCKS4: {
|
||||||
|
if(c->address.sa.sa_family != AF_INET) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char s4req[9 + (proxyuser ? strlen(proxyuser) : 0)];
|
||||||
|
s4req[0] = 4;
|
||||||
|
s4req[1] = 1;
|
||||||
|
memcpy(s4req + 2, &c->address.in.sin_port, 2);
|
||||||
|
memcpy(s4req + 4, &c->address.in.sin_addr, 4);
|
||||||
|
if(proxyuser)
|
||||||
|
strcpy(s4req + 8, proxyuser);
|
||||||
|
s4req[sizeof s4req - 1] = 0;
|
||||||
|
c->tcplen = 8;
|
||||||
|
return send_meta(c, s4req, sizeof s4req);
|
||||||
|
}
|
||||||
|
case PROXY_SOCKS5: {
|
||||||
|
int len = 3 + 6 + (c->address.sa.sa_family == AF_INET ? 4 : 16);
|
||||||
|
c->tcplen = 2;
|
||||||
|
if(proxypass)
|
||||||
|
len += 3 + strlen(proxyuser) + strlen(proxypass);
|
||||||
|
char s5req[len];
|
||||||
|
int i = 0;
|
||||||
|
s5req[i++] = 5;
|
||||||
|
s5req[i++] = 1;
|
||||||
|
if(proxypass) {
|
||||||
|
s5req[i++] = 2;
|
||||||
|
s5req[i++] = 1;
|
||||||
|
s5req[i++] = strlen(proxyuser);
|
||||||
|
strcpy(s5req + i, proxyuser);
|
||||||
|
i += strlen(proxyuser);
|
||||||
|
s5req[i++] = strlen(proxypass);
|
||||||
|
strcpy(s5req + i, proxypass);
|
||||||
|
i += strlen(proxypass);
|
||||||
|
c->tcplen += 2;
|
||||||
|
} else {
|
||||||
|
s5req[i++] = 0;
|
||||||
|
}
|
||||||
|
s5req[i++] = 5;
|
||||||
|
s5req[i++] = 1;
|
||||||
|
s5req[i++] = 0;
|
||||||
|
if(c->address.sa.sa_family == AF_INET) {
|
||||||
|
s5req[i++] = 1;
|
||||||
|
memcpy(s5req + i, &c->address.in.sin_addr, 4);
|
||||||
|
i += 4;
|
||||||
|
memcpy(s5req + i, &c->address.in.sin_port, 2);
|
||||||
|
i += 2;
|
||||||
|
c->tcplen += 10;
|
||||||
|
} else if(c->address.sa.sa_family == AF_INET6) {
|
||||||
|
s5req[i++] = 3;
|
||||||
|
memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
|
||||||
|
i += 16;
|
||||||
|
memcpy(s5req + i, &c->address.in6.sin6_port, 2);
|
||||||
|
i += 2;
|
||||||
|
c->tcplen += 22;
|
||||||
|
} else {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(i > len)
|
||||||
|
abort();
|
||||||
|
return send_meta(c, s5req, sizeof s5req);
|
||||||
|
}
|
||||||
|
case PROXY_SOCKS4A:
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
|
||||||
|
return false;
|
||||||
|
case PROXY_EXEC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool send_id(connection_t *c) {
|
bool send_id(connection_t *c) {
|
||||||
gettimeofday(&c->start, NULL);
|
gettimeofday(&c->start, NULL);
|
||||||
|
|
||||||
|
@ -54,6 +140,10 @@ bool send_id(connection_t *c) {
|
||||||
minor = myself->connection->protocol_minor;
|
minor = myself->connection->protocol_minor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(proxytype)
|
||||||
|
if(!send_proxyrequest(c))
|
||||||
|
return false;
|
||||||
|
|
||||||
return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
|
return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,12 @@
|
||||||
|
|
||||||
rmode_t routing_mode = RMODE_ROUTER;
|
rmode_t routing_mode = RMODE_ROUTER;
|
||||||
fmode_t forwarding_mode = FMODE_INTERNAL;
|
fmode_t forwarding_mode = FMODE_INTERNAL;
|
||||||
|
bmode_t broadcast_mode = BMODE_MST;
|
||||||
bool decrement_ttl = false;
|
bool decrement_ttl = false;
|
||||||
bool directonly = false;
|
bool directonly = false;
|
||||||
bool priorityinheritance = false;
|
bool priorityinheritance = false;
|
||||||
int macexpire = 600;
|
int macexpire = 600;
|
||||||
bool overwrite_mac = false;
|
bool overwrite_mac = false;
|
||||||
bool broadcast = true;
|
|
||||||
mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
|
mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
|
||||||
bool pcap = false;
|
bool pcap = false;
|
||||||
|
|
||||||
|
@ -447,7 +447,7 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
|
||||||
if(!checklength(source, packet, ether_size + ip_size))
|
if(!checklength(source, packet, ether_size + ip_size))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(broadcast && (((packet->data[30] & 0xf0) == 0xe0) || (
|
if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
|
||||||
packet->data[30] == 255 &&
|
packet->data[30] == 255 &&
|
||||||
packet->data[31] == 255 &&
|
packet->data[31] == 255 &&
|
||||||
packet->data[32] == 255 &&
|
packet->data[32] == 255 &&
|
||||||
|
@ -744,7 +744,7 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(broadcast && packet->data[38] == 255)
|
if(broadcast_mode && packet->data[38] == 255)
|
||||||
broadcast_packet(source, packet);
|
broadcast_packet(source, packet);
|
||||||
else
|
else
|
||||||
route_ipv6_unicast(source, packet);
|
route_ipv6_unicast(source, packet);
|
||||||
|
@ -834,8 +834,7 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
|
||||||
subnet = lookup_subnet_mac(NULL, &dest);
|
subnet = lookup_subnet_mac(NULL, &dest);
|
||||||
|
|
||||||
if(!subnet) {
|
if(!subnet) {
|
||||||
if(broadcast)
|
broadcast_packet(source, packet);
|
||||||
broadcast_packet(source, packet);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,18 @@ typedef enum fmode_t {
|
||||||
FMODE_KERNEL,
|
FMODE_KERNEL,
|
||||||
} fmode_t;
|
} fmode_t;
|
||||||
|
|
||||||
|
typedef enum bmode_t {
|
||||||
|
BMODE_NONE = 0,
|
||||||
|
BMODE_MST,
|
||||||
|
BMODE_DIRECT,
|
||||||
|
} bmode_t;
|
||||||
|
|
||||||
extern rmode_t routing_mode;
|
extern rmode_t routing_mode;
|
||||||
extern fmode_t forwarding_mode;
|
extern fmode_t forwarding_mode;
|
||||||
|
extern bmode_t broadcast_mode;
|
||||||
extern bool decrement_ttl;
|
extern bool decrement_ttl;
|
||||||
extern bool directonly;
|
extern bool directonly;
|
||||||
extern bool overwrite_mac;
|
extern bool overwrite_mac;
|
||||||
extern bool broadcast;
|
|
||||||
extern bool priorityinheritance;
|
extern bool priorityinheritance;
|
||||||
extern int macexpire;
|
extern int macexpire;
|
||||||
extern bool pcap;
|
extern bool pcap;
|
||||||
|
|
10
src/utils.c
10
src/utils.c
|
@ -137,15 +137,17 @@ int b64encode(const char *src, char *dst, int length) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char *winerror(int err) {
|
const char *winerror(int err) {
|
||||||
static char buf[1024], *newline;
|
static char buf[1024], *ptr;
|
||||||
|
|
||||||
|
ptr = buf + sprintf(buf, "(%d) ", err);
|
||||||
|
|
||||||
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
|
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
|
||||||
strncpy(buf, "(unable to format errormessage)", sizeof(buf));
|
strncpy(buf, "(unable to format errormessage)", sizeof(buf));
|
||||||
};
|
};
|
||||||
|
|
||||||
if((newline = strchr(buf, '\r')))
|
if((ptr = strchr(buf, '\r')))
|
||||||
*newline = '\0';
|
*ptr = '\0';
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue