]> jfr.im git - solanum.git/blobdiff - authd/providers/opm.c
Revert "authd: change to lists instead of dictionaries for various things"
[solanum.git] / authd / providers / opm.c
index 1dd064f36b037f1899c16eb1707027fdc3e9724b..560eecee09f121df929b1b4994f5bd642b1ed17b 100644 (file)
  * 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
@@ -36,32 +39,31 @@ 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];
@@ -70,26 +72,41 @@ struct opm_listener
        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)
@@ -133,7 +150,7 @@ read_opm_reply(rb_fde_t *F, void *data)
        ssize_t len;
 
        lrb_assert(auth != NULL);
-       lookup = get_provider_data(auth, PROVIDER_OPM);
+       lookup = get_provider_data(auth, SELF_PID);
        lrb_assert(lookup != NULL);
 
        if((len = rb_read(F, readbuf, sizeof(readbuf))) < 0 && rb_ignore_errno(errno))
@@ -168,7 +185,7 @@ read_opm_reply(rb_fde_t *F, void *data)
                        /* 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;
                }
        }
@@ -245,24 +262,18 @@ accept_opm(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t len, voi
 /* 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;
+       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;
-
-       lrb_assert(scan != NULL);
-
-       auth = scan->auth;
-       lookup = get_provider_data(auth, PROVIDER_OPM);
-
-       memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */
+       }
 
        switch(GET_SS_FAMILY(&auth->c_addr))
        {
@@ -271,51 +282,63 @@ socks4_connected(rb_fde_t *F, int error, void *data)
                        /* 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 */
+               if(!proxy->ipv6)
+                       /* Welp, too bad */
+                       goto end;
+
+               if(listeners[LISTEN_IPV6].F == NULL)
+                       /* They cannot respond to us */
+                       goto end;
+
+               break;
 #endif
        default:
                goto end;
        }
 
+       proxy->callback(scan);
+
+end:
+       rb_close(scan->F);
+       rb_dlinkDelete(&scan->node, &lookup->scans);
+       rb_free(scan);
+}
+
+static void
+socks4_connected(struct opm_scan *scan)
+{
+       struct auth_client *auth = scan->auth;
+       struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
+       uint8_t sendbuf[9]; /* Size we're building */
+       uint8_t *c = sendbuf;
+
+       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)
-               goto end;
+               return;
 
        /* Send note */
        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
-socks5_connected(rb_fde_t *F, int error, void *data)
+socks5_connected(struct opm_scan *scan)
 {
-       struct opm_scan *scan = data;
-       struct opm_lookup *lookup;
-       struct opm_listener *listener;
-       struct auth_client *auth;
+       struct auth_client *auth = scan->auth;
+       struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
        uint8_t sendbuf[25]; /* Size we're building */
        uint8_t *c = sendbuf;
-       ssize_t ret;
-
-       if(error || !opm_enable)
-               goto end;
-
-       lrb_assert(scan != NULL);
 
        auth = scan->auth;
-       lookup = get_provider_data(auth, PROVIDER_OPM);
+       lookup = get_provider_data(auth, SELF_PID);
 
        /* Build the version header and socks request
         * version header (3 bytes): version, number of auth methods, auth type (0 for none)
@@ -326,149 +349,94 @@ socks5_connected(rb_fde_t *F, int error, void *data)
        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;
+       struct auth_client *auth = scan->auth;
+       struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
        char sendbuf[128]; /* A bit bigger than we need but better safe than sorry */
        char *c = sendbuf;
-       ssize_t ret;
-
-       if(error || !opm_enable)
-               goto end;
-
-       lrb_assert(scan != NULL);
-
-       auth = scan->auth;
-       lookup = get_provider_data(auth, PROVIDER_OPM);
-
-       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 = get_provider_data(auth, 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;
 
        lrb_assert(lookup != NULL);
 
-       switch(proxy->proto)
-       {
-       case PROTO_SOCKS4:
 #ifdef RB_IPV6
-               /* SOCKS4 is IPv4 only */
-               if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
+       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));
@@ -488,14 +456,12 @@ establish_connection(struct auth_client *auth, struct opm_proxy *proxy)
                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
@@ -617,14 +583,15 @@ opm_scan(struct auth_client *auth)
 
        lrb_assert(auth != NULL);
 
-       lookup = get_provider_data(auth, 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);
        }
 
@@ -633,19 +600,21 @@ opm_scan(struct auth_client *auth)
 
 /* 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 = get_provider_data(auth, 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);
@@ -654,29 +623,31 @@ blacklists_initiate(struct auth_client *auth, provider_t provider)
 static bool
 opm_start(struct auth_client *auth)
 {
-       lrb_assert(get_provider_data(auth, PROVIDER_OPM) == NULL);
+       uint32_t rdns_pid, ident_pid;
+
+       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");
                return true;
-       }
 
-       set_provider_data(auth, PROVIDER_BLACKLIST, rb_malloc(sizeof(struct opm_lookup)));
+       set_provider_data(auth, SELF_PID, rb_malloc(sizeof(struct opm_lookup)));
 
-       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 */
+       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);
+       set_provider_running(auth, SELF_PID);
        return true;
 }
 
 static void
 opm_cancel(struct auth_client *auth)
 {
-       struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM);
+       struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
 
        if(lookup != NULL)
        {
@@ -694,9 +665,9 @@ opm_cancel(struct auth_client *auth)
 
                rb_free(lookup);
 
-               set_provider_data(auth, PROVIDER_OPM, NULL);
-               auth->timeout[PROVIDER_OPM] = 0;
-               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);
        }
 }
 
@@ -759,18 +730,16 @@ set_opm_enabled(const char *key __unused, int parc __unused, const char **parv)
        {
                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);
                }
        }
 
@@ -811,17 +780,21 @@ create_opm_scanner(const char *key __unused, int parc __unused, const char **par
        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:
@@ -869,7 +842,7 @@ delete_opm_scanner(const char *key __unused, int parc __unused, const char **par
        RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
        {
                rb_dlink_node *ptr;
-               struct opm_lookup *lookup = get_provider_data(auth, PROVIDER_OPM);
+               struct opm_lookup *lookup = get_provider_data(auth, SELF_PID);
 
                if(lookup == NULL)
                        continue;
@@ -920,12 +893,27 @@ delete_opm_scanner_all(const char *key __unused, int parc __unused, const char *
        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);
+
+#ifdef RB_IPV6
+       if(listeners[LISTEN_IPV6].F != NULL)
+               rb_close(listeners[LISTEN_IPV6].F);
+#endif
+
+       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 },
@@ -934,10 +922,12 @@ struct auth_opts_handler opm_options[] =
 
 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,
 };