limit the number of concurrent connections for all four protocols
[profile/ivi/pulseaudio-panda.git] / polyp / protocol-native.c
index 7af8bdd..12ecfae 100644 (file)
@@ -4,7 +4,7 @@
   This file is part of polypaudio.
  
   polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published
+  it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
  
@@ -13,7 +13,7 @@
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
  
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with polypaudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 #include "subscribe.h"
 #include "log.h"
 #include "autoload.h"
+#include "authkey-prop.h"
+#include "strlist.h"
+#include "props.h"
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 5
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 10
 
 struct connection;
 struct pa_protocol_native;
@@ -97,6 +106,7 @@ struct connection {
     struct pa_idxset *record_streams, *output_streams;
     uint32_t rrobin_index;
     struct pa_subscription *subscription;
+    struct pa_time_event *auth_timeout_event;
 };
 
 struct pa_protocol_native {
@@ -106,6 +116,7 @@ struct pa_protocol_native {
     struct pa_socket_server *server;
     struct pa_idxset *connections;
     uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+    int auth_cookie_in_property;
 };
 
 static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk);
@@ -370,6 +381,9 @@ static void connection_free(struct connection *c) {
 
     if (c->subscription)
         pa_subscription_free(c->subscription);
+
+    if (c->auth_timeout_event)
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
     
     pa_xfree(c);
 }
@@ -742,6 +756,10 @@ static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag
         }
         
         c->authorized = 1;
+        if (c->auth_timeout_event) {
+            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+            c->auth_timeout_event = NULL;
+        }
     }
     
     pa_pstream_send_simple_ack(c->pstream, tag);
@@ -1339,7 +1357,7 @@ static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, u
     pa_tagstruct_puts(reply, PACKAGE_NAME);
     pa_tagstruct_puts(reply, PACKAGE_VERSION);
     pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
-    pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+    pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
     pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
 
     n = pa_namereg_get_default_sink_name(c->protocol->core);
@@ -1869,7 +1887,7 @@ static void command_get_autoload_info_list(struct pa_pdispatch *pd, uint32_t com
         struct pa_autoload_entry *a;
         void *state = NULL;
 
-        while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state)))
+        while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
             autoload_fill_tagstruct(reply, a);
     }
     
@@ -1969,14 +1987,37 @@ static void client_kill_cb(struct pa_client *c) {
 
 /*** socket server callbacks ***/
 
+static void auth_timeout(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct connection *c = userdata;
+    assert(m && tv && c && c->auth_timeout_event == e);
+
+    if (!c->authorized)
+        connection_free(c);
+}
+
 static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
     struct pa_protocol_native *p = userdata;
     struct connection *c;
     assert(io && p);
 
+    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
     c = pa_xmalloc(sizeof(struct connection));
 
     c->authorized =!! p->public;
+
+    if (!c->authorized) {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        tv.tv_sec += AUTH_TIMEOUT;
+        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+    } else
+        c->auth_timeout_event = NULL;
+    
     c->protocol = p;
     assert(p->core);
     c->client = pa_client_new(p->core, "NATIVE", "Client");
@@ -2008,6 +2049,32 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
 
 /*** module entry points ***/
 
+static int load_key(struct pa_protocol_native*p, const char*fn) {
+    assert(p);
+
+    p->auth_cookie_in_property = 0;
+    
+    if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
+        pa_log(__FILE__": using already loaded auth cookie.\n");
+        pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+        p->auth_cookie_in_property = 1;
+        return 0;
+    }
+    
+    if (!fn)
+        fn = PA_NATIVE_COOKIE_FILE;
+
+    if (pa_authkey_load_from_home(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0)
+        return -1;
+
+    pa_log(__FILE__": loading cookie from disk.\n");
+
+    if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
+        p->auth_cookie_in_property = 1;
+        
+    return 0;
+}
+
 static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struct pa_module *m, struct pa_modargs *ma) {
     struct pa_protocol_native *p;
     int public = 0;
@@ -2019,16 +2086,16 @@ static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struc
     }
     
     p = pa_xmalloc(sizeof(struct pa_protocol_native));
+    p->core = c;
+    p->module = m;
+    p->public = public;
+    p->server = NULL;
 
-    if (pa_authkey_load_from_home(pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), p->auth_cookie, sizeof(p->auth_cookie)) < 0) {
+    if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0) {
         pa_xfree(p);
         return NULL;
     }
 
-    p->module = m;
-    p->public = public;
-    p->server = NULL;
-    p->core = c;
     p->connections = pa_idxset_new(NULL, NULL);
     assert(p->connections);
 
@@ -2036,6 +2103,7 @@ static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struc
 }
 
 struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+    char t[256];
     struct pa_protocol_native *p;
 
     if (!(p = protocol_new_internal(core, m, ma)))
@@ -2043,6 +2111,13 @@ struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct p
     
     p->server = server;
     pa_socket_server_set_callback(p->server, on_connection, p);
+
+    if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+        struct pa_strlist *l;
+        l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME);
+        l = pa_strlist_prepend(l, t);
+        pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+    }
     
     return p;
 }
@@ -2055,9 +2130,26 @@ void pa_protocol_native_free(struct pa_protocol_native *p) {
         connection_free(c);
     pa_idxset_free(p->connections, NULL, NULL);
 
-    if (p->server)
+    if (p->server) {
+        char t[256];
+        
+        if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+            struct pa_strlist *l;
+            l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+            l = pa_strlist_remove(l, t);
+
+            if (l)
+                pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+            else
+                pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+        }
+        
         pa_socket_server_unref(p->server);
-    
+    }
+
+    if (p->auth_cookie_in_property)
+        pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
     pa_xfree(p);
 }