1 /*  Open DC Hub - A Linux/Unix version of the Direct Connect hub.
2  *  Copyright (C) 2002,2003  Jonatan Nilsson
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #if HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #if HAVE_FCNTL_H
32 # include <fcntl.h>
33 #endif
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44 #include <errno.h>
45 #include <sys/ipc.h>
46 #include <sys/shm.h>
47 #include <sys/sem.h>
48 
49 #include "main.h"
50 #include "utils.h"
51 #include "fileio.h"
52 #include "userlist.h"
53 #include "network.h"
54 
55 /* the number of the next char which is the char c */
56 /* Maybe it's better with just strchr(buf) - buf */
cut_string(char * buf,char c)57 int cut_string(char *buf, char c)
58 {
59    int i;
60    i = 0;
61    while((buf[i] != c) && (buf[i] != (char)NULL))
62      i++;
63    if(buf[i] == c)
64      return i;
65    return -1;
66 }
67 
68 /* Appends to the end of a string */
69 /* NO checking of buf size, so it MUST be big enough */
70 /* The string usually has to be zeroed before this can be used */
sprintfa(char * buf,const char * format,...)71 void sprintfa(char *buf, const char *format, ...)
72 {
73    if(format)
74      {
75 	va_list args;
76 	va_start(args, format);
77 	vsprintf(buf + strlen(buf), format, args);
78 	va_end(args);
79      }
80 }
81 
82 /* Send formated string to user. Maximum length is 4096 chars */
uprintf(struct user_t * user,char * format,...)83 void uprintf(struct user_t *user, char *format, ...)
84 {
85    char buf[4096];
86    if(format)
87      {
88 	va_list args;
89 	va_start(args, format);
90 	vsnprintf(buf, 4095, format, args);
91 	va_end(args);
92 	send_to_user(buf, user);
93      }
94 }
95 
96 /* Removes \r:s and \n:s and bs:s from end of a string */
trim_string(char * buf)97 int trim_string(char *buf)
98 {
99    int len;
100    if(!buf)
101      return 0;
102    if(buf[0] == '\0')
103      return 1;
104    for(len = strlen(buf)-1; len >=0; len--)
105      {
106 	if((buf[len] == '\n') || (buf[len] == '\r') || (buf[len] == ' '))
107 	  buf[len] = '\0';
108 	else
109 	  break;
110      }
111    return 1;
112 }
113 
114 /* Counts number of users which are included in type.  */
count_users(int type)115 int count_users(int type)
116 {
117    int count;
118    struct user_t *non_human;
119    struct sock_t *human_user;
120 
121    count = 0;
122    non_human = non_human_user_list;
123    human_user = human_sock_list;
124 
125    /* Start with non-human users.  */
126    while(non_human != NULL)
127      {
128 	if((type & non_human->type) != 0)
129 	  count++;
130 
131 	non_human = non_human->next;
132      }
133 
134    /* And the human users.  */
135    while(human_user != NULL)
136      {
137 	if((type & human_user->user->type) != 0)
138 	  count++;
139 
140 	human_user = human_user->next;
141      }
142 
143    return count;
144 }
145 
146 
147 /* Count all users in the whole hub.  */
count_all_users(void)148 int count_all_users(void)
149 {
150    char *buf;
151    int spaces=0, entries=0;
152 
153    sem_take(user_list_sem);
154 
155    /* Attach to the shared segment */
156    if((buf = (char *)shmat(get_user_list_shm_id(), NULL, 0))
157       == (char *)-1)
158      {
159 	logprintf(1, "Error - In count_all_users()/shmat(): ");
160 	logerror(1, errno);
161 	sem_give(user_list_sem);
162 	quit = 1;
163 	return -1;
164      }
165 
166    if(sscanf(buf, "%d %d", &spaces, &entries) != 2)
167      {
168 	logprintf(1, "Error - In count_all_users(): Couldn't get number of entries\n");
169 	shmdt(buf);
170 	sem_give(user_list_sem);
171 	quit = 1;
172 	return -1;
173      }
174    shmdt(buf);
175    sem_give(user_list_sem);
176    return entries;
177 }
178 
179 /* Sends initial $Lock string to client */
send_lock(struct user_t * user)180 void send_lock(struct user_t *user)
181 {
182    char lock_string[150];
183    int len;
184    int i, j, k;
185 
186    if(check_key != 0)
187      {
188 	create_lock:
189 
190 	memset(lock_string, 0, sizeof(lock_string));
191 
192 	srand(time(NULL));
193 
194 	/* This will be the seed value used to compare the clients lock key with
195 	 * the correct one */
196 	user->key = rand();
197 	srand(user->key);
198 	len = 48 + rand()%30;
199 
200 	sprintf(lock_string, "$Lock ");
201 
202 	lock_string[6] = '%' + rand()%('z'-'%');
203 	/* The values in the lock should vary from '%' to 'z' */
204 	for(k = 7; k <= len+6; k++)
205 	  {
206 	     lock_string[k] = '%' + rand()%('z'-'%');
207 	     i = (((unsigned int)(lock_string[k]     ))&0xff)
208 	       ^ (((unsigned int)(lock_string[k-1]   ))&0xff);
209 	     j = ((i | (i << 8)) >> 4)&0xff;
210 	     if(j == '\0')
211 	       k--;
212 	  }
213 
214 	i = (((unsigned int)(lock_string[6]     ))&0xff)
215 	  ^ (((unsigned int)(lock_string[6+len]  ))&0xff)
216 	    ^ (((unsigned int)(lock_string[6+len-1]))&0xff)
217 	      ^ 0x05;
218 	j = ((i | (i << 8)) >> 4)&0xff;
219 	if(j == '\0')
220 	  goto create_lock;
221 
222 	sprintfa(lock_string, " Pk=");
223 	k += 4;
224 	for(j = 0; j <= 15; j++)
225 	  lock_string[k+j] = '%' + rand()%('z'-'%');
226 	sprintfa(lock_string, "|");
227      }
228    else
229      sprintf(lock_string, "$Lock Sending_key_isn't_neccessary,_key_won't_be_checked. Pk=Same_goes_here.|");
230    send_to_user(lock_string, user);
231 }
232 
233 /* Checks the key sent from the client */
validate_key(char * buf,struct user_t * user)234 int validate_key(char *buf, struct user_t *user)
235 {
236    char lock_string[150];
237    char key[400];
238    int i, j, k, len;
239    int lockp;
240 
241    /* First, reconstruct the lock string that was sent to the client */
242    srand(user->key);
243    len = 48 + rand()%30;
244 
245    lock_string[0] = '%' + rand()%('z'-'%');
246    for(k = 1; k <= len; k++)
247      {
248 	lock_string[k] = '%' + rand()%('z'-'%');
249 	i = (((unsigned int)(lock_string[k]     ))&0xff)
250 	  ^ (((unsigned int)(lock_string[k-1]   ))&0xff);
251 	j = ((i | (i << 8)) >> 4)&0xff;
252 	if(j == '\0')
253 	  k--;
254      }
255 
256    lockp = 0;
257 
258    /* The first character is computed differently */
259    i = (((unsigned int)(lock_string[lockp]     ))&0xff)
260      ^ (((unsigned int)(lock_string[len]  ))&0xff)
261        ^ (((unsigned int)(lock_string[len-1]))&0xff)
262 	 ^ 0x05;
263    j = ((i | (i << 8)) >> 4)&0xff;
264 
265    switch(j)
266      {
267       case 5:
268 	sprintf(key, "/%%DCN005%%/");
269 	break;
270 
271       case 36:
272 	sprintf(key, "/%%DCN036%%/");
273 	break;
274 
275       case 96:
276 	sprintf(key, "/%%DCN096%%/");
277 	break;
278 
279       default:
280 	sprintf(key, "%c", j);
281 	break;
282      }
283    lockp++;
284 
285    for(k = lockp; k <= len; k++)
286      {
287 	i = (((unsigned int)(lock_string[k]     ))&0xff)
288 	  ^ (((unsigned int)(lock_string[k-1]   ))&0xff);
289 
290 	j = ((i | (i << 8)) >> 4)&0xff;
291 
292 	switch(j)
293 	  {
294 	   case 5:
295 	     sprintfa(key, "/%%DCN005%%/");
296 	     break;
297 
298 	   case '$':
299 	     sprintfa(key, "/%%DCN036%%/");
300 	     break;
301 
302 	   case 96:
303 	     sprintfa(key, "/%%DCN096%%/");
304 	     break;
305 
306 	   default:
307 	     sprintfa(key, "%c", j);
308 	     break;
309 	  }
310      }
311    if(strncmp(buf+5, key, strlen(key)) != 0)
312       return 0;
313    user->type = NON_LOGGED;
314    return 1;
315 }
316 
317 /* Puts users hostname in buffy.  */
get_users_hostname(char * nick,char * buffy)318 void get_users_hostname(char *nick, char *buffy)
319 {
320    char *buf, *bufp;
321    char temp_nick[MAX_NICK_LEN+1];
322    char temp_host[MAX_HOST_LEN+1];
323    int spaces=0, entries=0;
324    int i;
325 
326    sem_take(user_list_sem);
327 
328    /* Attach to the shared segment.  */
329    if((buf = (char *)shmat(get_user_list_shm_id(), NULL, 0))
330       == (char *)-1)
331      {
332 	logprintf(1, "Error - In get_users_hostname()/shmat(): ");
333 	logerror(1, errno);
334 	sem_give(user_list_sem);
335 	quit = 1;
336 	return;
337      }
338 
339    if(sscanf(buf, "%d %d", &spaces, &entries) != 2)
340      {
341 	logprintf(1, "Error - In remove_user_from_list(): Couldn't get number of entries\n");
342 	shmdt(buf);
343 	sem_give(user_list_sem);
344 	quit = 1;
345 	return;
346      }
347 
348    bufp = buf + 30;
349 
350    for(i = 1; i <= spaces; i++)
351      {
352 	if(*bufp != '\0')
353 	  {
354 	     sscanf(bufp, "%50s %120s", temp_nick, temp_host);
355 	     if((strncasecmp(temp_nick, nick, strlen(nick)) == 0)
356 		&& (strlen(nick) == strlen(temp_nick)))
357 	       {
358 		  /* The user is here, so detach and put the hostname in the
359 		   * buf.  */
360 		  sprintf(buffy, "%s", temp_host);
361 		  shmdt(buf);
362 		  sem_give(user_list_sem);
363 		  return;
364 	       }
365 	  }
366 	bufp += USER_LIST_ENT_SIZE;
367      }
368 
369    /* If user wasn't found, put null in returning string */
370    *buffy = '\0';
371 
372    shmdt(buf);
373    sem_give(user_list_sem);
374 }
375 
376 /* Returns a hash value from a users nickname. It's important that this
377  * function generates values as random as possible, but also stays fast.  */
get_hash(char * nick)378 int get_hash(char *nick)
379 {
380    register char *s1, *s2;
381    register int i = 0;
382    register int hash = 0;
383 
384    /* First char in nick.  */
385    s1 = nick;
386 
387    /* Last char in nick.  */
388    s2 = nick + strlen(nick) - 1;
389 
390    do
391      {
392 	hash |= ((*s1 & 0x1) << i);
393 	i++;
394 	hash |= ((*s2 & 0x1) << i);
395 	i++;
396 	s1++;
397 	s2--;
398      } while((s1 <= s2) && (hash < max_sockets));
399 
400    while(hash > max_sockets)
401      hash >>= 1;
402 
403    return hash;
404 }
405 
406 /* Initializes the semaphore sem to state "taken".  */
init_sem(int * sem)407 int init_sem(int *sem)
408 {
409    union my_semun arg;
410 
411    *sem = semget(IPC_PRIVATE, 1, 0600);
412    if(*sem < 0)
413      {
414 	logprintf(1, "Error - In init_sem()/semget(): ");
415 	logerror(1, errno);
416 	return -1;
417      }
418 
419    arg.val = 1;
420    if(semctl(*sem, 0, SETVAL, arg) == -1)
421      {
422 	logprintf(1, "Error - In init_sem()/semctl(): ");
423 	logerror(1, errno);
424 	return -1;
425      }
426    return 1;
427 }
428 
429 /* Takes a semaphore.  */
sem_take(int sem)430 void sem_take(int sem)
431 {
432    int ret;
433    struct sembuf buf;
434 
435    memset(&buf, 0, sizeof(struct sembuf));
436 
437    buf.sem_num = 0;
438    buf.sem_op = -1;
439    buf.sem_flg = 0;
440 
441    /* Take the semaphore.  */
442    while(((ret = semop(sem, &buf, 1)) < 0) && (errno == EINTR))
443      logprintf(1, "Error - In sem_take/semop(): Interrupted system call. Trying again.\n");
444 
445    if(ret < 0)
446      {
447 	logprintf(1, "Error - In sem_take()/semop(): ");
448 	logerror(1, errno);
449 	quit = 1;
450      }
451 }
452 
453 /* Gives a semaphore.  */
sem_give(int sem)454 void sem_give(int sem)
455 {
456    int ret;
457    struct sembuf buf;
458 
459    memset(&buf, 0, sizeof(struct sembuf));
460 
461    buf.sem_num = 0;
462    buf.sem_op = 1;
463    buf.sem_flg = 0;
464 
465    /* Give the semaphore.  */
466    while(((ret = semop(sem, &buf, 1)) < 0) && (errno == EINTR))
467      logprintf(1, "Error - In sem_give/semop(): Interrupted system call. Trying again.\n");
468 
469    if(ret < 0)
470      {
471 	logprintf(1, "Error - In sem_give()/semop(): ");
472 	logerror(1, errno);
473 	quit = 1;
474      }
475 }
476 
477 /* Initializes the shared memory segment with to total share.  */
init_share_shm(void)478 int init_share_shm(void)
479 {
480    long long *init_share;
481 
482    /* Get identifier for the shared data segment */
483    if((total_share_shm = shmget(IPC_PRIVATE, sizeof(long long), 0600)) < 0)
484      {
485 	logprintf(1, "Error - In init_share_shm()/shmget(): ");
486 	logerror(1, errno);
487 	return -1;
488      }
489 
490    /* Attach to the shared segment */
491    if((init_share = shmat(total_share_shm, NULL, 0))
492       == (long long *) -1)
493      {
494 	logprintf(1, "Error - In init_share_shm()/shmat(): ");
495 	logerror(1, errno);
496 	shmctl(total_share_shm, IPC_RMID, NULL);
497 	return -1;
498      }
499 
500    *init_share = 0;
501    shmdt((char *)init_share);
502 
503    return 1;
504 }
505 
506 /* Adds to the total share, can be both positive and negative.  */
add_total_share(long long add)507 void add_total_share(long long add)
508 {
509    long long *share_size;
510 
511    /* Take the semaphore.  */
512    sem_take(total_share_sem);
513 
514    /* Attach to the shared memory segment.  */
515    if((share_size = shmat(total_share_shm, NULL, 0))
516        == (long long *) -1)
517      {
518 	logprintf(1, "Error - In init_share_shm()/shmat(): ");
519 	logerror(1, errno);
520 	shmctl(total_share_shm, IPC_RMID, NULL);
521 	semctl(total_share_sem, 0, IPC_RMID, NULL);
522 	return;
523      }
524 
525    /* Add to the segment.  */
526    *share_size += add;
527 
528    /* Dettach from the segment.  */
529    shmdt((char *)share_size);
530 
531    /* And give back the semaphore.  */
532    sem_give(total_share_sem);
533 }
534 
535 /* Get the current total share.  */
get_total_share(void)536 long long get_total_share(void)
537 {
538    long long *share_size;
539    long long ret;
540 
541    /* Take the semaphore.  */
542    sem_take(total_share_sem);
543 
544    /* Attach to the shared memory segment.  */
545    if((share_size = shmat(total_share_shm, NULL, 0))
546        == (long long *) -1)
547      {
548 	logprintf(1, "Error - In get_total_share()/shmat(): ");
549 	logerror(1, errno);
550 	sem_give(total_share_sem);
551 	return 0;
552      }
553 
554    /* Get the return value.  */
555    ret = *share_size;
556 
557    /* Dettach from the segment.  */
558    shmdt((char *)share_size);
559 
560    /* And give back the semaphore.  */
561    sem_give(total_share_sem);
562    return ret;
563 }
564 
565 /* Get the uptime of the hub in seconds.  */
get_uptime(void)566 double get_uptime(void)
567 {
568    return difftime(time(NULL), hub_start_time);
569 }
570 
571 /* Returns 1 if buf1 is a match in buf2, wich can contain wildcards.  */
match_with_wildcards(char * buf1,char * buf2)572 int match_with_wildcards(char *buf1, char *buf2)
573 {
574    int k = 0;
575    char token[MAX_HOST_LEN+1];
576    char *fbuf, *ubuf;
577 
578    /* The '*' is allowed as wildcard. To ban a nick with a '*'in it, it has to be
579     * escaped with a '\'. '\':s also have to be escaped with '\'.  */
580 
581    fbuf = buf2;
582    ubuf = buf1;
583    while((*fbuf != '\0') && (*ubuf != '\0'))
584      {
585 	/* If we are escaping a '\' or a '*':  */
586 	if(*fbuf == '\\')
587 	  {
588 	     /* After a '\', only '*' and '\' is allowed.  */
589 	     fbuf++;
590 	     if(*fbuf == '\0')
591 	       return 0;
592 	     if(*fbuf == '\\')
593 	       {
594 		  if(*ubuf != '\\')
595 		    return 0;
596 	       }
597 	     else if(*fbuf == '*')
598 	       {
599 		  if(*ubuf != '*')
600 		    return 0;
601 	       }
602 	     else
603 	       return 0;
604 	  }
605 
606 	/* If we have a wildcard.  */
607 	if(*fbuf == '*')
608 	  {
609 	     fbuf++;
610 	     if(*fbuf == '\0')
611 	       return 1;
612 
613 	     if(*fbuf == '*')
614 	       return 0;
615 
616 	     if((k = cut_string(fbuf, '*')+1) == 0)
617 	       k = strlen(fbuf)+1;
618 
619 	     if(k == 1)
620 	       k = strlen(fbuf);
621 
622 	     if((strncmp(fbuf, ubuf, k) == 0) && (*(ubuf+k) == '\0'))
623 	       return 1;
624 
625 	     strncpy(token, fbuf, k-1);
626 	     *(token + k - 1) = '\0';
627 	     if(strstr(ubuf, token) == NULL)
628 		  return 0;
629 
630 	     fbuf += k-2;
631 	     ubuf = strstr(ubuf, token) + k - 2;
632 	  }
633 
634 	/* No wildcard, just compare the strings character by
635 	 * character.  */
636 	else if(*fbuf != *ubuf)
637 	     return 0;
638 
639 	fbuf++;
640 	ubuf++;
641      }
642 
643    if((*ubuf == '\0') && ((*fbuf == '*') && (*(fbuf+1) == '\0')))
644      return 1;
645 
646    if(*fbuf != *ubuf)
647      {
648 	fbuf = buf2+strlen(buf2)-1;
649 	ubuf = buf1+strlen(buf1)-1;
650 	while((fbuf >= buf2) && (*fbuf != '*'))
651 	  {
652 	     if(*fbuf != *ubuf)
653 	       return 0;
654 	     fbuf--;
655 	     ubuf--;
656 	  }
657      }
658    return 1;
659 }
660 
661 /* This function prints all names in the hashtable for a certain process. It
662  * can be commented out and can be used anywhere. */
663 /*void print_usernames(void)
664 {
665    struct user_t *user;
666    int i;
667    int count = 1;
668 
669    sem_take(user_list_sem);
670    logprintf(1, "Printing all users in process %d\n", getpid());
671 
672    for(i = 0; i <= max_sockets; i++)
673      {
674 	user = human_hash_table[i];
675 	while(user != NULL)
676 	  {
677 	     logprintf(1, "User %d:s nick: %s\n", count, user->nick);
678 	     count++;
679 	     user = user->next;
680 	  }
681      }
682    sem_give(user_list_sem);
683 }*/
684