1 /*
2     Terminal Mixer - multi-point multi-user access to terminal applications
3     Copyright (C) 2007  Lluís Batlle i Rossell
4 
5     Please find the license in the provided COPYING file.
6 */
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <netinet/in.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <sys/select.h>
15 
16 #include "main.h"
17 #include "handlers.h"
18 
19 static int served_sockets = 0;
20 
21 static int listen_socket = -1; /* not listening */
22 static int connected_sockets = 0;
23 static int *conn_sockets = 0;
24 
start_listening(int new)25 static void start_listening(int new)
26 {
27     int ls;
28     struct sockaddr_in addr;
29     int res;
30 
31     assert(new > 0);
32 
33     ls = socket(AF_INET, SOCK_STREAM, 0);
34     if (ls == -1)
35         error("Cannot create the tcp listen socket in the server");
36 
37     {
38         int on = 1;
39         res = setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
40         if (res == -1)
41             error("Cannot set SO_REUSEADDR");
42     }
43 
44     addr.sin_family = AF_INET;
45     addr.sin_port = htons(command_line.tcp_port);
46     addr.sin_addr.s_addr = htonl(INADDR_ANY);
47 
48     res = bind(ls, (struct sockaddr *) & addr, sizeof(addr));
49     if (res == -1)
50         error("Error binding tcp to port %i", command_line.tcp_port);
51 
52     /* NUANCE: 0 backlog. Why should we assure future connections? */
53     res = listen(ls, 0);
54     if (res == -1)
55         error("Error listening on the binded tcp socket");
56 
57     listen_socket = ls;
58 }
59 
s_tcp_update_served(int new)60 void s_tcp_update_served(int new)
61 {
62     if (new > served_sockets && new > 0 )
63     {
64         conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets) * new);
65         if (listen_socket == -1) /* not listening */
66         {
67             start_listening(new);
68         }
69         served_sockets = new;
70     }
71     else if (new < served_sockets)
72     {
73         not_implemented("new < served_sockets at s_tcp_update_served");
74     }
75 }
76 
s_tcp_shutdown()77 void s_tcp_shutdown()
78 {
79     int i;
80 
81     if (listen_socket != -1)
82     {
83         close(listen_socket);
84     }
85 
86     for (i=0; i < connected_sockets; ++i)
87     {
88         close(conn_sockets[i]);
89     }
90     connected_sockets = 0;
91     served_sockets = 0;
92 }
93 
s_tcp_prepare_read_fdset(fd_set * read_set,int * maxfd)94 void s_tcp_prepare_read_fdset(fd_set *read_set, int *maxfd)
95 {
96     int i;
97 
98     FD_SET(listen_socket, read_set);
99     *maxfd = max(*maxfd, listen_socket);
100 
101     for (i=0; i < connected_sockets; ++i)
102     {
103         /* We only accept if we don't have any
104          * connetion opened. */
105         FD_SET(conn_sockets[i], read_set);
106         *maxfd = max(*maxfd, conn_sockets[i]);
107     }
108 }
109 
remove_conn_socket(int n)110 static void remove_conn_socket(int n)
111 {
112     int i;
113 
114     assert(n >= 0);
115 
116     for(i=n+1; i<connected_sockets; ++i)
117     {
118         conn_sockets[i-1] = conn_sockets[i];
119     }
120 
121     connected_sockets -= 1;
122 
123     /* We shrink the memory until we have an array for served_sockets */
124     if (served_sockets <= connected_sockets)
125     {
126         conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets)
127                 * connected_sockets);
128     }
129 }
130 
accept_connection(int ls)131 static int accept_connection(int ls)
132 {
133     int cs;
134     cs = accept(ls, 0, 0);
135     if (cs == -1)
136         not_implemented("accept tcp socket error check");
137 
138     /* TODO: Prepare unblocking sockets if needed */
139 
140     return cs;
141 }
142 
s_tcp_process_read_fdset(fd_set * read_set)143 void s_tcp_process_read_fdset(fd_set *read_set)
144 {
145     int i;
146     /* Active streams */
147     for (i=0; i < connected_sockets; ++i)
148     {
149         if (FD_ISSET(conn_sockets[i], read_set))
150         {
151             int res;
152             res = read(conn_sockets[i], stream_buffer,
153                     stream_buffer_size);
154             if (res == 0)
155             {
156                 close(conn_sockets[i]);
157                 remove_conn_socket(i);
158             } else
159             {
160                 app_control_remote_send_to_stdin(stream_buffer, res);
161             }
162         }
163     }
164 
165     /* Listen connection */
166     if (FD_ISSET(listen_socket, read_set))
167     {
168         if (served_sockets > connected_sockets)
169         {
170             int s;
171             s = accept_connection(listen_socket);
172             conn_sockets[connected_sockets++] = s;
173             welcome_new_client_socket(s);
174         } else
175         {
176             int s;
177             s = accept_connection(listen_socket);
178             close(s);
179         }
180     }
181 }
182 
s_tcp_send_to_connected(const char * buffer,size_t size)183 void s_tcp_send_to_connected(const char *buffer, size_t size)
184 {
185     int i;
186 
187     for (i=0; i < connected_sockets; ++i)
188     {
189         write(conn_sockets[i], buffer, size);
190     }
191 }
192