1 /*************************************************************************/
2 /*                                                                       */
3 /*                Centre for Speech Technology Research                  */
4 /*                     University of Edinburgh, UK                       */
5 /*                       Copyright (c) 1996,1997                         */
6 /*                        All Rights Reserved.                           */
7 /*                                                                       */
8 /*  Permission is hereby granted, free of charge, to use and distribute  */
9 /*  this software and its documentation without restriction, including   */
10 /*  without limitation the rights to use, copy, modify, merge, publish,  */
11 /*  distribute, sublicense, and/or sell copies of this work, and to      */
12 /*  permit persons to whom this work is furnished to do so, subject to   */
13 /*  the following conditions:                                            */
14 /*   1. The code must retain the above copyright notice, this list of    */
15 /*      conditions and the following disclaimer.                         */
16 /*   2. Any modifications must be clearly marked as such.                */
17 /*   3. Original authors' names are not deleted.                         */
18 /*   4. The authors' names are not used to endorse or promote products   */
19 /*      derived from this software without specific prior written        */
20 /*      permission.                                                      */
21 /*                                                                       */
22 /*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
23 /*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
24 /*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
25 /*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
26 /*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
27 /*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
28 /*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
29 /*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
30 /*  THIS SOFTWARE.                                                       */
31 /*                                                                       */
32 /*************************************************************************/
33 /*                Author :  Alan Black and Richard Tobin                 */
34 /*                Date   :  December 1996                                */
35 /*-----------------------------------------------------------------------*/
36 /*                                                                       */
37 /* Socket support to run Festival as a server process                    */
38 /*                                                                       */
39 /* The original socket code is derived Richard Tobin's scokpipe/pipesock */
40 /* examples, but have be substantially changed to be more general        */
41 /* for Festival                                                          */
42 /*                                                                       */
43 /* The logging, access control, file transfer stuff are all new          */
44 /*                                                                       */
45 /*=======================================================================*/
46 #include <cstdlib>
47 #include <cstdio>
48 #include <cerrno>
49 #include <sys/types.h>
50 #include <cstring>
51 #include <ctime>
52 #include "EST_unix.h"
53 #include "EST_socket.h"
54 #include "festival.h"
55 #include "festivalP.h"
56 
57 #define DEFAULT_MAX_CLIENTS 10
58 
59 /* The folloing gives a server that never forks */
60 /* and only accepts one client at a time.  This is good for */
61 /* OSs with an expensive implementation of fork and or waitpid (e.g. NT) */
62 #ifdef WIN32
63 #define SINGLE_CLIENT 1
64 #endif
65 
66 
67 static int client_access_check(int fd,int client);
68 static EST_String log_time_stamp(int client);
69 static void log_message(int client,const char *message);
70 
71 int ft_server_socket = -1;
72 ostream *cslog = NULL;
73 
festival_start_server(int port)74 int festival_start_server(int port)
75 {
76     // Never exits except by signals
77     struct sockaddr_in serv_addr;
78     int fd, fd1;
79     int statusp;
80     int client_name=0;
81     int max_clients, num_clients, pid;
82     LISP lmax_clients, llog_file;
83 
84     lmax_clients = siod_get_lval("server_max_client",NULL);
85     if (lmax_clients != NULL)
86 	max_clients = get_c_int(lmax_clients);
87     else
88 	max_clients = DEFAULT_MAX_CLIENTS;
89     num_clients = 0;
90     llog_file = siod_get_lval("server_log_file",NULL);
91     if (llog_file == NIL)
92 	cslog = cdebug;
93     else if (llog_file == siod_get_lval("t",NULL))
94 	cslog = &cout;
95     else
96 	cslog = new ofstream(get_c_string(llog_file),ios::app);
97 
98     if (!socket_initialise())
99     {
100 	festival_error();
101       }
102 
103     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
104 
105    if (NOT_A_SOCKET(fd))
106     {
107       int n = socket_error();
108       cerr << "socket: socket failed (" << n << ")\n";
109 
110 	festival_error();
111     }
112    int one = 1;
113 
114    if (setsockopt(fd, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) < 0)
115      {
116        cerr << "socket: SO_REUSEADDR failed" << endl;
117 	festival_error();
118      }
119 
120     memset(&serv_addr, 0, sizeof(serv_addr));
121     serv_addr.sin_family = AF_INET;
122     serv_addr.sin_port = htons(port);
123     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
124 
125     if (::bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0)
126     {
127 	cerr << "socket: bind failed" << endl;
128 	festival_error();
129     }
130 
131     if (listen(fd, 5) != 0)
132     {
133 	cerr << "socket: listen failed" << endl;
134 	festival_error();
135     }
136 
137 #if SINGLE_CLIENT
138     log_message(0,EST_String("Festival server (non-forking) started on port ")+
139 		itoString(port));
140 #else
141     log_message(0,EST_String("Festival server started on port ")+
142 		itoString(port));
143 #endif
144 
145     fflush(stdout);
146     fflush(stderr);
147     fflush(stdin);
148 
149     while(1)                          // never exits except by signals
150     {
151 	if((fd1 = accept(fd, 0, 0)) < 0)
152 	{
153 	    cerr << "socket: accept failed";
154 	    festival_error();
155 	}
156 
157 	client_name++;
158 	if (client_access_check(fd1,client_name) == FALSE)
159 	{
160 	    close(fd1);
161 	    continue;
162 	}
163 #ifdef SINGLE_CLIENT
164 	ft_server_socket = fd1;
165 	repl_from_socket(fd1);
166 	log_message(client_name,"disconnected");
167 #else
168 	num_clients++;
169 
170 	// Fork new image of festival and call interpreter
171 	if (num_clients > max_clients)
172 	{
173 	    log_message(client_name,"failed: too many clients");
174 	    num_clients--;
175 	}
176 	else if ((pid=fork()) == 0)
177 	{
178 	    ft_server_socket = fd1;
179 	    repl_from_socket(fd1);
180 	    log_message(client_name,"disconnected");
181 	    exit(0);
182 	}
183 	else if (pid < 0)
184 	{
185 	    log_message(client_name,"failed to fork new client");
186 	    num_clients--;
187 	}
188 
189 	while ((num_clients > 0) &&  (waitpid(0,&statusp,WNOHANG) != 0))
190 	    num_clients--;
191 #endif
192 
193 	close(fd1);
194     }
195 
196     return 0;
197 }
198 
client_access_check(int fd,int client)199 static int client_access_check(int fd,int client)
200 {
201     // Check client against various possible checks to see if they
202     // are allowed access to the server
203     LISP passwd, access_list, deny_list;
204     int client_access = TRUE;
205     struct sockaddr_in peer;
206     socklen_t addrlen=sizeof(peer);
207     struct hostent *clienthost;
208     const char *client_hostname;
209     const char *client_hostnum;
210     const char *reason = "";
211 
212     getpeername(fd,(struct sockaddr *)&peer,&addrlen);
213     clienthost = gethostbyaddr((char *)&peer.sin_addr,
214 			       sizeof(peer.sin_addr),AF_INET);
215     client_hostnum = inet_ntoa(peer.sin_addr);
216     if (streq(client_hostnum,"0.0.0.0") || streq(client_hostnum,"127.0.0.1")) // its me !
217 	client_hostname = "localhost";
218     else if (clienthost == 0)	                       // failed to get a name
219 	client_hostname = client_hostnum;
220     else
221 	client_hostname = clienthost->h_name;
222 
223     if (((deny_list = siod_get_lval("server_deny_list",NULL)) != NIL) &&
224 	(siod_regex_member_str(client_hostname,deny_list) != NIL))
225     {
226 	client_access = FALSE;
227 	reason = "in deny list";
228     }
229     else if ((access_list = siod_get_lval("server_access_list",NULL)) != NIL)
230     {
231 	client_access = FALSE;	                       // by default now
232 	reason = "not in access list";
233 	if (siod_regex_member_str(client_hostname,access_list) != NIL)
234 	{
235 	    reason = "";
236 	    client_access = TRUE;
237 	}
238     }
239 
240     passwd = siod_get_lval("server_passwd",NULL);
241     if ((client_access == TRUE) && (passwd != NULL))
242     {
243 	char *client_passwd = walloc(char,strlen(get_c_string(passwd))+1);
244 	read(fd,client_passwd,strlen(get_c_string(passwd)));
245 	client_passwd[strlen(get_c_string(passwd))] = '\0';
246 	if (streq(get_c_string(passwd),client_passwd))
247 	    client_access = TRUE;
248 	else
249 	{
250 	    client_access = FALSE;
251 	    reason = "bad passwd";
252 	}
253 	wfree(client_passwd);
254     }
255     char *message = walloc(char,20+strlen(client_hostname)+strlen(reason));
256 
257     if (client_access == TRUE)
258     {
259 	sprintf(message,"accepted from %s",client_hostname);
260 	log_message(client,message);
261     }
262     else
263     {
264 	sprintf(message,"rejected from %s %s",client_hostname,reason);
265 	log_message(client,message);
266     }
267 
268     wfree(message);
269 
270     return client_access;
271 
272 }
273 
log_message(int client,const char * message)274 static void log_message(int client, const char *message)
275 {
276     // log the message in log file
277 
278     *cslog << log_time_stamp(client) << message << endl;
279 }
280 
log_time_stamp(int client)281 static EST_String log_time_stamp(int client)
282 {
283     // returns a string with client id, time and date
284     char lst[1024];
285     time_t thetime = time(0);
286     char *cthetime = ctime(&thetime);
287     cthetime[24] = '\0';	// get rid of \n
288 
289     if (client == 0)
290 	sprintf(lst,"server    %s : ",cthetime);
291     else
292 	sprintf(lst,"client(%d) %s : ",client,cthetime);
293 
294     return lst;
295 }
296 
297 
298 
299