* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "rb_lib.h"
#include "stdinc.h"
+#include "rb_lib.h"
+#include "defaults.h"
#include "setup.h"
#include "authd.h"
#include "notice.h"
#include "provider.h"
+#define SELF_PID (opm_provider.id)
+
#define OPM_READSIZE 128
typedef enum protocol_t
PROTO_HTTPS_CONNECT,
} protocol_t;
+/* Lookup data associated with auth client */
struct opm_lookup
{
rb_dlink_list scans; /* List of scans */
bool in_progress;
};
+struct opm_scan;
+typedef void (*opm_callback_t)(struct opm_scan *);
+
+/* A proxy scanner */
struct opm_proxy
{
char note[16];
protocol_t proto;
uint16_t port;
- bool ssl;
+ bool ssl; /* Connect to proxy with SSL */
+ bool ipv6; /* Proxy supports IPv6 */
- rb_dlink_node node;
-};
-
-struct opm_scan
-{
- struct auth_client *auth;
-
- struct opm_proxy *proxy;
- rb_fde_t *F; /* fd for scan */
+ opm_callback_t callback;
rb_dlink_node node;
};
+/* A listener for proxy replies */
struct opm_listener
{
char ip[HOSTIPLEN];
rb_fde_t *F;
};
+/* An individual proxy scan */
+struct opm_scan
+{
+ struct auth_client *auth;
+ rb_fde_t *F; /* fd for scan */
+
+ struct opm_proxy *proxy; /* Associated proxy */
+ struct opm_listener *listener; /* Associated listener */
+
+ rb_dlink_node node;
+};
+
/* Proxies that we scan for */
static rb_dlink_list proxy_scanners;
static ACCB accept_opm;
static PF read_opm_reply;
-static CNCB socks4_connected;
-static CNCB socks5_connected;
+static CNCB opm_connected;
static void opm_cancel(struct auth_client *auth);
static bool create_listener(const char *ip, uint16_t port);
-static int opm_timeout = 10;
+static int opm_timeout = OPM_TIMEOUT_DEFAULT;
static bool opm_enable = false;
-#define LISTEN_IPV4 0
-#define LISTEN_IPV6 1
+enum
+{
+ LISTEN_IPV4,
+ LISTEN_IPV6,
+ LISTEN_LAST,
+};
/* IPv4 and IPv6 */
-static struct opm_listener listeners[2];
+static struct opm_listener listeners[LISTEN_LAST];
static inline protocol_t
get_protocol_from_string(const char *str)
return NULL;
}
+/* This is called when an open proxy connects to us */
static void
read_opm_reply(rb_fde_t *F, void *data)
{
char readbuf[OPM_READSIZE];
ssize_t len;
- if(auth == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
- {
- rb_close(F);
- return;
- }
+ lrb_assert(auth != NULL);
+ lookup = get_provider_data(auth, SELF_PID);
+ lrb_assert(lookup != NULL);
if((len = rb_read(F, readbuf, sizeof(readbuf))) < 0 && rb_ignore_errno(errno))
{
/* No longer needed, client is going away */
rb_free(lookup);
- reject_client(auth, PROVIDER_OPM, readbuf, "Open proxy detected");
+ reject_client(auth, SELF_PID, readbuf, "Open proxy detected");
break;
}
}
}
break;
}
-#ifdef RB_IPV6
case AF_INET6:
{
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&localaddr, *c = (struct sockaddr_in6 *)&auth->c_addr;
}
break;
}
-#endif
default:
warn_opers(L_CRIT, "OPM: unknown address type in listen function");
exit(EX_PROVIDER_ERROR);
/* Scanners */
static void
-socks4_connected(rb_fde_t *F, int error, void *data)
+opm_connected(rb_fde_t *F, int error, void *data)
{
struct opm_scan *scan = data;
- struct opm_lookup *lookup;
- struct auth_client *auth;
- uint8_t sendbuf[9]; /* Size we're building */
- uint8_t *c = sendbuf;
- ssize_t ret;
-
- if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
- return;
+ struct opm_proxy *proxy = scan->proxy;
+ struct auth_client *auth = scan->auth;
+ struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
if(error || !opm_enable)
+ {
+ //notice_client(scan->auth->cid, "*** Scan not connected: %s", proxy->note);
goto end;
-
- memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */
+ }
switch(GET_SS_FAMILY(&auth->c_addr))
{
/* They cannot respond to us */
goto end;
- memcpy(c, &(((struct sockaddr_in *)&listeners[LISTEN_IPV4].addr)->sin_port), 2); c += 2; /* Port */
- memcpy(c, &(((struct sockaddr_in *)&listeners[LISTEN_IPV4].addr)->sin_addr.s_addr), 4); c += 4; /* Address */
break;
-#ifdef RB_IPV6
case AF_INET6:
- /* socks4 doesn't support IPv6 */
-#endif
- default:
- goto end;
- }
+ if(!proxy->ipv6)
+ /* Welp, too bad */
+ goto end;
- *c = '\x00'; /* No userid */
+ if(listeners[LISTEN_IPV6].F == NULL)
+ /* They cannot respond to us */
+ goto end;
- /* Send header */
- if(rb_write(scan->F, sendbuf, sizeof(sendbuf)) < 0)
+ break;
+ default:
goto end;
+ }
- /* Send note */
- if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) < 0)
- goto end;
+ proxy->callback(scan);
end:
rb_close(scan->F);
}
static void
-socks5_connected(rb_fde_t *F, int error, void *data)
+socks4_connected(struct opm_scan *scan)
{
- struct opm_scan *scan = data;
- struct opm_lookup *lookup;
- struct opm_listener *listener;
- struct auth_client *auth;
- uint8_t sendbuf[25]; /* Size we're building */
+ uint8_t sendbuf[9]; /* Size we're building */
uint8_t *c = sendbuf;
- ssize_t ret;
- if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
+ memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */
+ memcpy(c, &(((struct sockaddr_in *)&scan->listener->addr)->sin_port), 2); c += 2; /* Port */
+ memcpy(c, &(((struct sockaddr_in *)&scan->listener->addr)->sin_addr.s_addr), 4); c += 4; /* Address */
+ *c = '\x00'; /* No userid */
+
+ /* Send header */
+ if(rb_write(scan->F, sendbuf, sizeof(sendbuf)) < 0)
return;
- if(error || !opm_enable)
- goto end;
+ /* Send note */
+ if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) < 0)
+ return;
+}
+
+static void
+socks5_connected(struct opm_scan *scan)
+{
+ struct auth_client *auth = scan->auth;
+ uint8_t sendbuf[25]; /* Size we're building */
+ uint8_t *c = sendbuf;
/* Build the version header and socks request
* version header (3 bytes): version, number of auth methods, auth type (0 for none)
switch(GET_SS_FAMILY(&auth->c_addr))
{
case AF_INET:
- listener = &listeners[LISTEN_IPV4];
- if(!listener->F)
- goto end;
-
*(c++) = '\x01'; /* Address type (1 = IPv4) */
- memcpy(c, &(((struct sockaddr_in *)&listener->addr)->sin_addr.s_addr), 4); c += 4;/* Address */
- memcpy(c, &(((struct sockaddr_in *)&listener->addr)->sin_port), 2); c += 2; /* Port */
+ memcpy(c, &(((struct sockaddr_in *)&scan->listener->addr)->sin_addr.s_addr), 4); c += 4; /* Address */
+ memcpy(c, &(((struct sockaddr_in *)&scan->listener->addr)->sin_port), 2); c += 2; /* Port */
break;
-#ifdef RB_IPV6
case AF_INET6:
- listener = &listeners[LISTEN_IPV6];
- if(!listener->F)
- goto end;
-
*(c++) = '\x04'; /* Address type (4 = IPv6) */
- memcpy(c, ((struct sockaddr_in6 *)&listener->addr)->sin6_addr.s6_addr, 16); c += 16; /* Address */
- memcpy(c, &(((struct sockaddr_in6 *)&listener->addr)->sin6_port), 2); c += 2; /* Port */
+ memcpy(c, ((struct sockaddr_in6 *)&scan->listener->addr)->sin6_addr.s6_addr, 16); c += 16; /* Address */
+ memcpy(c, &(((struct sockaddr_in6 *)&scan->listener->addr)->sin6_port), 2); c += 2; /* Port */
break;
-#endif
default:
- goto end;
+ return;
}
/* Send header */
if(rb_write(scan->F, sendbuf, (size_t)(sendbuf - c)) <= 0)
- goto end;
+ return;
/* Now the note in a separate write */
if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) <= 0)
- goto end;
-
-end:
- rb_close(scan->F);
- rb_dlinkDelete(&scan->node, &lookup->scans);
- rb_free(scan);
+ return;
}
static void
-http_connect_connected(rb_fde_t *F, int error, void *data)
+http_connect_connected(struct opm_scan *scan)
{
- struct opm_scan *scan = data;
- struct opm_lookup *lookup;
- struct opm_listener *listener;
- struct auth_client *auth;
char sendbuf[128]; /* A bit bigger than we need but better safe than sorry */
- char *c = sendbuf;
- ssize_t ret;
-
- if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
- return;
-
- if(error || !opm_enable)
- goto end;
-
- switch(GET_SS_FAMILY(&auth->c_addr))
- {
- case AF_INET:
- listener = &listeners[LISTEN_IPV4];
- if(!listener->F)
- goto end;
- break;
-#ifdef RB_IPV6
- case AF_INET6:
- listener = &listeners[LISTEN_IPV6];
- if(!listener->F)
- goto end;
- break;
-#endif
- default:
- goto end;
- }
/* Simple enough to build */
- snprintf(sendbuf, sizeof(sendbuf), "CONNECT %s:%hu HTTP/1.0\r\n\r\n", listener->ip, listener->port);
+ snprintf(sendbuf, sizeof(sendbuf), "CONNECT %s:%hu HTTP/1.0\r\n\r\n", scan->listener->ip, scan->listener->port);
/* Send request */
if(rb_write(scan->F, sendbuf, strlen(sendbuf)) <= 0)
- goto end;
+ return;
/* Now the note in a separate write */
if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) <= 0)
- goto end;
+ return;
/* MiroTik needs this, and as a separate write */
if(rb_write(scan->F, "\r\n", 2) <= 0)
- goto end;
-
-end:
- rb_close(scan->F);
- rb_dlinkDelete(&scan->node, &lookup->scans);
- rb_free(scan);
+ return;
}
/* Establish connections */
static inline void
establish_connection(struct auth_client *auth, struct opm_proxy *proxy)
{
- struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
- struct opm_listener *listener;
+ struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
struct opm_scan *scan = rb_malloc(sizeof(struct opm_scan));
+ struct opm_listener *listener;
struct rb_sockaddr_storage c_a, l_a;
int opt = 1;
- CNCB *callback;
- switch(proxy->proto)
+ lrb_assert(lookup != NULL);
+
+ if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
{
- case PROTO_SOCKS4:
-#ifdef RB_IPV6
- /* SOCKS4 is IPv4 only */
- if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
+ if(proxy->proto == PROTO_SOCKS4)
{
+ /* SOCKS4 doesn't support IPv6 */
rb_free(scan);
return;
}
-#endif
- callback = socks4_connected;
- break;
- case PROTO_SOCKS5:
- callback = socks5_connected;
- break;
- case PROTO_HTTP_CONNECT:
- case PROTO_HTTPS_CONNECT:
- callback = http_connect_connected;
- default:
- return;
- }
-
-#ifdef RB_IPV6
- if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
listener = &listeners[LISTEN_IPV6];
+ }
else
-#endif
listener = &listeners[LISTEN_IPV4];
+ if(listener->F == NULL)
+ {
+ /* We can't respond */
+ rb_free(scan);
+ return;
+ }
+
c_a = auth->c_addr; /* Client */
- l_a = listener->addr; /* Listener */
+ l_a = listener->addr; /* Listener (connect using its IP) */
scan->auth = auth;
scan->proxy = proxy;
+ scan->listener = listener;
if((scan->F = rb_socket(GET_SS_FAMILY(&auth->c_addr), SOCK_STREAM, 0, proxy->note)) == NULL)
{
warn_opers(L_WARN, "OPM: could not create OPM socket (proto %s): %s", proxy->note, strerror(errno));
rb_connect_tcp(scan->F,
(struct sockaddr *)&c_a,
(struct sockaddr *)&l_a,
- GET_SS_LEN(&l_a),
- callback, scan, opm_timeout);
+ opm_connected, scan, opm_timeout);
else
rb_connect_tcp_ssl(scan->F,
(struct sockaddr *)&c_a,
(struct sockaddr *)&l_a,
- GET_SS_LEN(&l_a),
- callback, scan, opm_timeout);
+ opm_connected, scan, opm_timeout);
}
static bool
SET_SS_PORT(&addr, htons(port));
-#ifdef RB_IPV6
if(GET_SS_FAMILY(&addr) == AF_INET6)
{
struct sockaddr_in6 *a1, *a2;
}
}
else
-#endif
{
struct sockaddr_in *a1, *a2;
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{
opm_cancel(auth);
+ /* auth is now invalid as we have no reference */
}
/* Copy data */
struct opm_lookup *lookup;
lrb_assert(auth != NULL);
- lookup = auth->data[PROVIDER_OPM];
- auth->timeout[PROVIDER_OPM] = rb_current_time() + opm_timeout;
+ lookup = get_provider_data(auth, SELF_PID);
+ set_provider_timeout_relative(auth, SELF_PID, opm_timeout);
lookup->in_progress = true;
RB_DLINK_FOREACH(ptr, proxy_scanners.head)
{
struct opm_proxy *proxy = ptr->data;
+ //notice_client(auth->cid, "*** Scanning for proxy type %s", proxy->note);
establish_connection(auth, proxy);
}
/* This is called every time a provider is completed as long as we are marked not done */
static void
-blacklists_initiate(struct auth_client *auth, provider_t provider)
+opm_initiate(struct auth_client *auth, uint32_t provider)
{
- struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
+ struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
+ uint32_t rdns_pid, ident_pid;
- lrb_assert(provider != PROVIDER_OPM);
- lrb_assert(!is_provider_done(auth, PROVIDER_OPM));
+ lrb_assert(provider != SELF_PID);
+ lrb_assert(!is_provider_done(auth, SELF_PID));
lrb_assert(rb_dlink_list_length(&proxy_scanners) > 0);
if(lookup == NULL || lookup->in_progress)
/* Nothing to do */
return;
- else if(!(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT)))
- /* Don't start until we've completed these */
+ else if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
+ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
+ /* Don't start until ident and rdns are finished (or not loaded) */
return;
else
opm_scan(auth);
static bool
opm_start(struct auth_client *auth)
{
- if(auth->data[PROVIDER_OPM] != NULL)
- return true;
+ uint32_t rdns_pid, ident_pid;
- if(!opm_enable || rb_dlink_list_length(&proxy_scanners) == 0)
- {
+ lrb_assert(get_provider_data(auth, SELF_PID) == NULL);
+
+ if(!opm_enable || rb_dlink_list_length(&proxy_scanners) == 0) {
/* Nothing to do... */
- notice_client(auth->cid, "*** Proxy scanning disabled, not scanning");
+ provider_done(auth, SELF_PID);
return true;
}
- auth->data[PROVIDER_BLACKLIST] = rb_malloc(sizeof(struct opm_lookup));
+ auth_client_ref(auth);
- if(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT))
- /* This probably can't happen but let's handle this case anyway */
+ set_provider_data(auth, SELF_PID, rb_malloc(sizeof(struct opm_lookup)));
+
+ if((!get_provider_id("rdns", &rdns_pid) || is_provider_done(auth, rdns_pid)) &&
+ (!get_provider_id("ident", &ident_pid) || is_provider_done(auth, ident_pid)))
+ {
+ /* Don't start until ident and rdns are finished (or not loaded) */
opm_scan(auth);
+ }
- set_provider_on(auth, PROVIDER_BLACKLIST);
return true;
}
static void
opm_cancel(struct auth_client *auth)
{
- struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
+ struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
if(lookup != NULL)
{
}
rb_free(lookup);
- provider_done(auth, PROVIDER_OPM);
+
+ set_provider_data(auth, SELF_PID, NULL);
+ set_provider_timeout_absolute(auth, SELF_PID, 0);
+ provider_done(auth, SELF_PID);
+
+ auth_client_unref(auth);
}
}
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{
opm_cancel(auth);
+ /* auth is now invalid as we have no reference */
}
}
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{
opm_cancel(auth);
+ /* auth is now invalid as we have no reference */
}
}
}
{
if(listeners[LISTEN_IPV4].ip[0] != '\0' && listeners[LISTEN_IPV4].port != 0)
{
- lrb_assert(listeners[LISTEN_IPV4].F == NULL);
-
- /* Pre-configured IP/port, just re-establish */
- create_listener(listeners[LISTEN_IPV4].ip, listeners[LISTEN_IPV4].port);
+ if(listeners[LISTEN_IPV4].F == NULL)
+ /* Pre-configured IP/port, just re-establish */
+ create_listener(listeners[LISTEN_IPV4].ip, listeners[LISTEN_IPV4].port);
}
if(listeners[LISTEN_IPV6].ip[0] != '\0' && listeners[LISTEN_IPV6].port != 0)
{
- lrb_assert(listeners[LISTEN_IPV6].F == NULL);
-
- /* Pre-configured IP/port, just re-establish */
- create_listener(listeners[LISTEN_IPV6].ip, listeners[LISTEN_IPV6].port);
+ if(listeners[LISTEN_IPV6].F == NULL)
+ /* Pre-configured IP/port, just re-establish */
+ create_listener(listeners[LISTEN_IPV6].ip, listeners[LISTEN_IPV6].port);
}
}
case PROTO_SOCKS4:
snprintf(proxy->note, sizeof(proxy->note), "socks4:%hu", proxy->port);
proxy->ssl = false;
+ proxy->callback = socks4_connected;
break;
case PROTO_SOCKS5:
snprintf(proxy->note, sizeof(proxy->note), "socks5:%hu", proxy->port);
proxy->ssl = false;
+ proxy->callback = socks5_connected;
break;
case PROTO_HTTP_CONNECT:
snprintf(proxy->note, sizeof(proxy->note), "httpconnect:%hu", proxy->port);
proxy->ssl = false;
+ proxy->callback = http_connect_connected;
break;
case PROTO_HTTPS_CONNECT:
snprintf(proxy->note, sizeof(proxy->note), "httpsconnect:%hu", proxy->port);
+ proxy->callback = http_connect_connected;
proxy->ssl = true;
break;
default:
if(find_proxy_scanner(proxy->proto, proxy->port) != NULL)
{
warn_opers(L_CRIT, "OPM: got a duplicate scanner: %s (port %hu)", parv[0], proxy->port);
- exit(EX_PROVIDER_ERROR);
+ rb_free(proxy);
+ return;
}
rb_dlinkAdd(proxy, &proxy->node, &proxy_scanners);
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{
rb_dlink_node *ptr;
- struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
+ struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
if(lookup == NULL)
continue;
+ auth_client_ref(auth);
+
RB_DLINK_FOREACH(ptr, lookup->scans.head)
{
struct opm_scan *scan = ptr->data;
break;
}
}
+
+ auth_client_unref(auth);
}
rb_dlinkDelete(&proxy->node, &proxy_scanners);
RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
{
opm_cancel(auth);
+ /* auth is now invalid as we have no reference */
}
opm_enable = false;
}
+static void
+delete_opm_listener_all(const char *key __unused, int parc __unused, const char **parv __unused)
+{
+ if(listeners[LISTEN_IPV4].F != NULL)
+ rb_close(listeners[LISTEN_IPV4].F);
+
+ if(listeners[LISTEN_IPV6].F != NULL)
+ rb_close(listeners[LISTEN_IPV6].F);
+
+ memset(&listeners, 0, sizeof(listeners));
+}
+
struct auth_opts_handler opm_options[] =
{
{ "opm_timeout", 1, add_conf_opm_timeout },
{ "opm_enabled", 1, set_opm_enabled },
{ "opm_listener", 2, set_opm_listener },
+ { "opm_listener_del_all", 0, delete_opm_listener_all },
{ "opm_scanner", 2, create_opm_scanner },
{ "opm_scanner_del", 2, delete_opm_scanner },
{ "opm_scanner_del_all", 0, delete_opm_scanner_all },
struct auth_provider opm_provider =
{
- .id = PROVIDER_OPM,
+ .name = "opm",
+ .letter = 'O',
.destroy = opm_destroy,
.start = opm_start,
.cancel = opm_cancel,
.timeout = opm_cancel,
+ .completed = opm_initiate,
.opt_handlers = opm_options,
};