Fix warning message
[freeradius.git] / src / main / listen.c
index e4446e8..2722d79 100644 (file)
@@ -72,9 +72,7 @@ typedef struct listen_socket_t {
         */
        fr_ipaddr_t     ipaddr;
        int             port;
-#ifdef SO_BINDTODEVICE
        const char              *interface;
-#endif
        RADCLIENT_LIST  *clients;
 } listen_socket_t;
 
@@ -88,7 +86,6 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
 {
 #ifdef WITH_DYNAMIC_CLIENTS
        int rcode;
-       listen_socket_t *sock;
        REQUEST *request;
        RADCLIENT *created;
 #endif
@@ -192,7 +189,8 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
                 *      can be defined.
                 */
                rad_assert(client->dynamic == 0);
-       } else {
+
+       } else if (!client->dynamic && client->rate_limit) {
                /*
                 *      The IP is unknown, so we've found an enclosing
                 *      network.  Enable DoS protection.  We only
@@ -209,12 +207,12 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
 
        request->listener = listener;
        request->client = client;
-       request->packet = rad_alloc(0);
-       if (!request->packet) {
+       request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */
+       if (!request->packet) {                         /* badly formed, etc */
                request_free(&request);
                goto unknown;
        }
-       request->reply = rad_alloc(0);
+       request->reply = rad_alloc_reply(request->packet);
        if (!request->reply) {
                request_free(&request);
                goto unknown;
@@ -233,23 +231,6 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
         *
         *      and create the RADCLIENT structure from that.
         */
-
-       sock = listener->data;
-       request->packet->sockfd = listener->fd;
-       request->packet->src_ipaddr = *ipaddr;
-       request->packet->src_port = 0; /* who cares... */
-       request->packet->dst_ipaddr = sock->ipaddr;
-       request->packet->dst_port = sock->port;
-
-       request->reply->sockfd = request->packet->sockfd;
-       request->reply->dst_ipaddr = request->packet->src_ipaddr;
-       request->reply->src_ipaddr = request->packet->dst_ipaddr;
-       request->reply->dst_port = request->packet->src_port;
-       request->reply->src_port = request->packet->dst_port;
-       request->reply->id = request->packet->id;
-       request->reply->code = 0; /* UNKNOWN code */
-
-       
        DEBUG("server %s {", request->server);
 
        rcode = module_authorize(0, request);
@@ -450,12 +431,10 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
 
        ADDSTRING(name);
 
-#ifdef SO_BINDTODEVICE
        if (sock->interface) {
                ADDSTRING(" interface ");
                ADDSTRING(sock->interface);
        }
-#endif
 
        ADDSTRING(" address ");
        
@@ -482,6 +461,8 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
        return 1;
 }
 
+extern int check_config;       /* radiusd.c */
+
 
 /*
  *     Parse an authentication or accounting socket.
@@ -532,16 +513,24 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        sock->ipaddr = ipaddr;
        sock->port = listen_port;
 
+       if (check_config) {
+               if (home_server_find(&sock->ipaddr, sock->port)) {
+                               char buffer[128];
+                               
+                               DEBUG("ERROR: We have been asked to listen on %s port %d, which is also listed as a home server.  This can create a proxy loop.",
+                                     ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
+                                     sock->port);
+                               return -1;
+               }
+
+               return 0;       /* don't do anything */
+       }
+
        /*
         *      If we can bind to interfaces, do so,
         *      else don't.
         */
        if (cf_pair_find(cs, "interface")) {
-#ifndef SO_BINDTODEVICE
-               cf_log_err(cf_sectiontoitem(cs),
-                          "System does not support binding to interfaces.  Delete this line from the configuration file.");
-               return -1;
-#else
                const char *value;
                CONF_PAIR *cp = cf_pair_find(cs, "interface");
 
@@ -553,7 +542,6 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                        return -1;
                }
                sock->interface = value;
-#endif
        }
 
        /*
@@ -1378,7 +1366,7 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
 
 #ifdef WITH_COMMAND_SOCKET
        /* TCP command socket */
-       { command_socket_parse, NULL,
+       { command_socket_parse, command_socket_free,
          command_domain_accept, command_domain_send,
          command_socket_print, command_socket_encode, command_socket_decode },
 #endif
@@ -1446,12 +1434,17 @@ static int listen_bind(rad_listen_t *this)
 
 #ifdef WITH_COA
                case RAD_LISTEN_COA:
-                       sock->port = PW_COA_UDP_PORT;
+                       svp = getservbyname ("radius-dynauth", "udp");
+                       if (svp != NULL) {
+                               sock->port = ntohs(svp->s_port);
+                       } else {
+                               sock->port = PW_COA_UDP_PORT;
+                       }
                        break;
 #endif
 
                default:
-                       radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind.");
+                       DEBUG("WARNING: Internal sanity check failed in binding to socket.  Ignoring problem.");
                        return -1;
                }
        }
@@ -1465,11 +1458,11 @@ static int listen_bind(rad_listen_t *this)
                return -1;
        }
                
-#ifdef SO_BINDTODEVICE
        /*
         *      Bind to a device BEFORE touching IP addresses.
         */
        if (sock->interface) {
+#ifdef SO_BINDTODEVICE
                struct ifreq ifreq;
                strcpy(ifreq.ifr_name, sock->interface);
 
@@ -1483,8 +1476,43 @@ static int listen_bind(rad_listen_t *this)
                               sock->interface, strerror(errno));
                        return -1;
                } /* else it worked. */
-       }
+#else
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+#ifdef HAVE_NET_IF_H
+               /*
+                *      Odds are that any system supporting "bind to
+                *      device" also supports IPv6, so this next bit
+                *      isn't necessary.  But it's here for
+                *      completeness.
+                *
+                *      If we're doing IPv6, and the scope hasn't yet
+                *      been defined, set the scope to the scope of
+                *      the interface.
+                */
+               if (sock->ipaddr.af == AF_INET6) {
+                       if (sock->ipaddr.scope == 0) {
+                               sock->ipaddr.scope = if_nametoindex(sock->interface);
+                               if (sock->ipaddr.scope == 0) {
+                                       close(this->fd);
+                                       radlog(L_ERR, "Failed finding interface %s: %s",
+                                              sock->interface, strerror(errno));
+                                       return -1;
+                               }
+                       } /* else scope was defined: we're OK. */
+               } else
+#endif
+#endif
+                               /*
+                                *      IPv4: no link local addresses,
+                                *      and no bind to device.
+                                */
+               {
+                       close(this->fd);
+                       radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
+                       return -1;
+               }
 #endif
+       }
 
 #ifdef WITH_UDPFROMTO
        /*
@@ -1524,6 +1552,31 @@ static int listen_bind(rad_listen_t *this)
        }
 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
 
+
+       if (sock->ipaddr.af == AF_INET) {
+               UNUSED int flag;
+               
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+               /*
+                *      Disable PMTU discovery.  On Linux, this
+                *      also makes sure that the "don't fragment"
+                *      flag is zero.
+                */
+               flag = IP_PMTUDISC_DONT;
+               setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER,
+                          &flag, sizeof(flag));
+#endif
+
+#if defined(IP_DONTFRAG)
+               /*
+                *      Ensure that the "don't fragment" flag is zero.
+                */
+               flag = 0;
+               setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG,
+                          &flag, sizeof(flag));
+#endif
+       }
+
        /*
         *      May be binding to priviledged ports.
         */
@@ -1618,9 +1671,6 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
 #ifdef WITH_VMPS
        case RAD_LISTEN_VQP:
 #endif
-#ifdef WITH_DHCP
-       case RAD_LISTEN_DHCP:
-#endif
 #ifdef WITH_COA
        case RAD_LISTEN_COA:
 #endif
@@ -1628,6 +1678,13 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
                memset(this->data, 0, sizeof(listen_socket_t));
                break;
 
+#ifdef WITH_DHCP
+       case RAD_LISTEN_DHCP:
+               this->data = rad_malloc(sizeof(dhcp_socket_t));
+               memset(this->data, 0, sizeof(dhcp_socket_t));
+               break;
+#endif
+
 #ifdef WITH_DETAIL
        case RAD_LISTEN_DETAIL:
                this->data = NULL;
@@ -1659,14 +1716,12 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
  */
 rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
 {
-       int last_proxy_port, port;
        rad_listen_t *this, *tmp, **last;
        listen_socket_t *sock, *old;
 
        /*
         *      Find an existing proxy socket to copy.
         */
-       last_proxy_port = 0;
        old = NULL;
        last = &mainconfig.listen;
        for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
@@ -1689,14 +1744,14 @@ rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
                        continue;
                }
                
-               if (sock->port > last_proxy_port) {
-                       last_proxy_port = sock->port + 1;
-               }
                if (!old) old = sock;
 
                last = &(tmp->next);
        }
 
+       this = listen_alloc(RAD_LISTEN_PROXY);
+       sock = this->data;
+
        if (!old) {
                /*
                 *      The socket MUST already exist if we're binding
@@ -1705,34 +1760,23 @@ rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
                 *      If we're initializing the server, it's OK for the
                 *      socket to NOT exist.
                 */
-               if (!exists) return NULL;
-
-               this = listen_alloc(RAD_LISTEN_PROXY);
-
-               sock = this->data;
-               sock->ipaddr = *ipaddr;
+               if (!exists) {
+                       DEBUG("WARNING: No previous template for proxy socket.  Source IP address may be chosen by the OS");
+               }
 
+               if (ipaddr->af != AF_UNSPEC) {
+                       sock->ipaddr = *ipaddr;
+               } else {
+                       memset(&sock->ipaddr, 0, sizeof(sock->ipaddr));
+                       sock->ipaddr.af = AF_INET; /* Oh well */
+               }
        } else {
-               this = listen_alloc(RAD_LISTEN_PROXY);
-               
-               sock = this->data;
                sock->ipaddr = old->ipaddr;
        }
 
-       /*
-        *      Keep going until we find an unused port.
-        */
-       for (port = last_proxy_port; port < 64000; port++) {
-               int rcode;
-
-               sock->port = port;
+       sock->port = 0;
 
-               rcode = listen_bind(this);
-               if (rcode < 0) {
-                       listen_free(&this);
-                       return NULL;
-               }
-               
+       if (listen_bind(this) >= 0) {
                /*
                 *      Add the new listener to the list of
                 *      listeners.
@@ -1741,6 +1785,7 @@ rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
                return this;
        }
 
+       DEBUG("Failed binding to new proxy socket");
        listen_free(&this);
        return NULL;
 }
@@ -1897,7 +1942,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                listen_socket_t *sock;
                server_ipaddr.af = AF_INET;
 
-               radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
+               radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
 
        bind_it:
 #ifdef WITH_VMPS