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