3 Copyright (C) 2004-2007 Chris Porter.
10 #include <sys/types.h>
11 #include <sys/socket.h>
14 #include <sys/ioctl.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
22 #include "../lib/sstring.h"
23 #include "../lib/irc_string.h"
24 #include "../core/config.h"
25 #include "../core/events.h"
26 #include "../lib/version.h"
27 #include "../core/schedule.h"
29 #include "nterfacer.h"
32 MODULE_VERSION("1.1p" PROTOCOL_VERSION
);
34 struct service_node
*tree
= NULL
;
35 struct esocket_events nterfacer_events
;
36 struct esocket
*nterfacer_sock
;
37 struct rline
*rlines
= NULL
;
38 unsigned short nterfacer_token
= BLANK_TOKEN
;
39 struct nterface_auto_log
*nrl
;
41 struct service_node
*ping
= NULL
;
43 struct permitted
*permits
;
46 int ping_handler(struct rline
*ri
, int argc
, char **argv
);
47 static void nterfacer_sendcallback(struct rline
*ri
, int error
, char *buf
);
51 int debug_mode
= getcopyconfigitemintpositive("nterfacer", "debug", 0);
53 nrl
= nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode
);
55 loaded
= load_permits();
56 nterface_log(nrl
, NL_INFO
, "Loaded %d permit%s successfully.", loaded
, loaded
==1?"":"s");
61 nterfacer_events
.on_accept
= nterfacer_accept_event
;
62 nterfacer_events
.on_line
= nterfacer_line_event
;
63 nterfacer_events
.on_disconnect
= NULL
;
65 nterfacer_token
= esocket_token();
67 ping
= register_service("nterfacer");
71 register_handler(ping
, "ping", 0, ping_handler
);
74 accept_fd
= setup_listening_socket();
76 nterface_log(nrl
, NL_ERROR
, "Unable to setup listening socket!");
78 nterfacer_sock
= esocket_add(accept_fd
, ESOCKET_UNIX_DOMAIN
, &nterfacer_events
, nterfacer_token
);
81 /* the main unix domain socket must NOT have a disconnect event. */
82 nterfacer_events
.on_disconnect
= nterfacer_disconnect_event
;
85 void free_handler(struct handler
*hp
) {
86 struct rline
*li
, *pi
= NULL
;
89 if(li
->handler
== hp
) {
91 esocket_write_line(li
->socket
, "%d,OE%d,%s", li
->id
, BF_UNLOADED
, "Service was unloaded.");
92 } else if(li
->callback
) {
93 nterfacer_sendcallback(li
, BF_UNLOADED
, "Service was unloaded.");
109 freesstring(hp
->command
);
113 void free_handlers(struct service_node
*tp
) {
114 struct handler
*hp
, *lp
;
116 for(hp
=tp
->handlers
;hp
;) {
126 struct service_node
*tp
, *lp
;
130 deregister_service(ping
);
140 if((accept_fd
!= -1) && nterfacer_sock
) {
141 esocket_clean_by_token(nterfacer_token
);
142 nterfacer_sock
= NULL
;
146 if(permits
&& permit_count
) {
147 for(i
=0;i
<permit_count
;i
++) {
148 freesstring(permits
[i
].hostname
);
149 freesstring(permits
[i
].password
);
156 nrl
= nterface_close_log(nrl
);
157 nscheckfreeall(POOL_NTERFACER
);
160 int load_permits(void) {
161 int loaded_lines
= 0, i
, j
;
162 struct permitted
*new_permits
, *resized
, *item
;
163 struct hostent
*host
;
164 array
*hostnamesa
, *passwordsa
;
165 sstring
**hostnames
, **passwords
;
167 hostnamesa
= getconfigitems("nterfacer", "hostname");
168 passwordsa
= getconfigitems("nterfacer", "password");
169 if(!hostnamesa
|| !passwordsa
)
171 if(hostnamesa
->cursi
!= passwordsa
->cursi
) {
172 nterface_log(nrl
, NL_ERROR
, "Different number of hostnames/passwords in config file.");
176 hostnames
= (sstring
**)hostnamesa
->content
;
177 passwords
= (sstring
**)passwordsa
->content
;
179 new_permits
= ntmalloc(hostnamesa
->cursi
* sizeof(struct permitted
));
180 memset(new_permits
, 0, hostnamesa
->cursi
* sizeof(struct permitted
));
183 for(i
=0;i
<hostnamesa
->cursi
;i
++) {
184 item
->hostname
= getsstring(hostnames
[i
]->content
, hostnames
[i
]->length
);
186 host
= gethostbyname(item
->hostname
->content
);
188 nterface_log(nrl
, NL_WARNING
, "Couldn't resolve hostname: %s (item %d).", item
->hostname
->content
, i
+ 1);
189 freesstring(item
->hostname
);
193 item
->ihost
= (*(struct in_addr
*)host
->h_addr_list
[0]).s_addr
;
194 for(j
=0;j
<loaded_lines
;j
++) {
195 if(new_permits
[j
].ihost
== item
->ihost
) {
196 nterface_log(nrl
, NL_WARNING
, "Host with items %d and %d is identical, dropping item %d.", j
+ 1, i
+ 1, i
+ 1);
202 freesstring(item
->hostname
);
206 item
->password
= getsstring(passwords
[i
]->content
, passwords
[i
]->length
);
207 nterface_log(nrl
, NL_DEBUG
, "Loaded permit, hostname: %s.", item
->hostname
->content
);
218 resized
= ntrealloc(new_permits
, sizeof(struct permitted
) * loaded_lines
);
225 permit_count
= loaded_lines
;
230 int setup_listening_socket(void) {
231 struct sockaddr_in sin
;
233 unsigned int opt
= 1;
235 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
237 /* also shamelessly ripped from proxyscan */
239 nterface_log(nrl
, NL_ERROR
, "Unable to open listening socket (%d).", errno
);
243 if(setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, (const char *) &opt
, sizeof(opt
)) != 0) {
244 nterface_log(nrl
, NL_ERROR
, "Unable to set SO_REUSEADDR on listen socket.");
248 /* Initialiase the addresses */
249 memset(&sin
, 0, sizeof(sin
));
250 sin
.sin_family
= AF_INET
;
251 sin
.sin_port
= htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT
));
253 if(bind(fd
, (struct sockaddr
*) &sin
, sizeof(sin
))) {
254 nterface_log(nrl
, NL_ERROR
, "Unable to bind listen socket (%d).", errno
);
261 if(ioctl(fd
, FIONBIO
, &opt
)) {
262 nterface_log(nrl
, NL_ERROR
, "Unable to set listen socket non-blocking.");
270 struct service_node
*register_service(char *name
) {
271 struct service_node
*np
= ntmalloc(sizeof(service_node
));
274 np
->name
= getsstring(name
, strlen(name
));
288 struct handler
*register_handler(struct service_node
*service
, char *command
, int args
, handler_function fp
) {
289 struct handler
*hp
= ntmalloc(sizeof(handler
));
292 hp
->command
= getsstring(command
, strlen(command
));
302 hp
->next
= service
->handlers
;
303 hp
->service
= service
;
304 service
->handlers
= hp
;
309 void deregister_handler(struct handler
*hl
) {
310 struct service_node
*service
= (struct service_node
*)hl
->service
;
311 struct handler
*np
, *lp
= NULL
;
312 for(np
=service
->handlers
;np
;lp
=np
,np
=np
->next
) {
317 service
->handlers
= np
->next
;
325 void deregister_service(struct service_node
*service
) {
326 struct service_node
*sp
, *lp
= NULL
;
328 for(sp
=tree
;sp
;lp
=sp
,sp
=sp
->next
) {
339 if(!sp
) /* already freed */
342 free_handlers(service
);
344 freesstring(service
->name
);
349 void nterfacer_accept_event(struct esocket
*socket
) {
350 struct sockaddr_in sin
;
351 unsigned int addrsize
= sizeof(sin
);
352 int newfd
= accept(socket
->fd
, (struct sockaddr
*)&sin
, &addrsize
), i
;
353 struct sconnect
*temp
;
354 struct permitted
*item
= NULL
;
355 struct esocket
*newsocket
;
356 unsigned int opt
= 1;
359 nterface_log(nrl
, NL_WARNING
, "Unable to accept nterfacer fd!");
363 if(ioctl(newfd
, FIONBIO
, &opt
)) {
364 nterface_log(nrl
, NL_ERROR
, "Unable to set accepted socket non-blocking.");
369 for(i
=0;i
<permit_count
;i
++) {
370 if(permits
[i
].ihost
== sin
.sin_addr
.s_addr
) {
377 nterface_log(nrl
, NL_INFO
, "Unauthorised connection from %s closed", inet_ntoa(sin
.sin_addr
));
382 temp
= (struct sconnect
*)ntmalloc(sizeof(struct sconnect
));
389 /* do checks on hostname first */
391 newsocket
= esocket_add(newfd
, ESOCKET_UNIX_DOMAIN_CONNECTED
, &nterfacer_events
, nterfacer_token
);
397 newsocket
->tag
= temp
;
399 nterface_log(nrl
, NL_INFO
, "New connection from %s.", item
->hostname
->content
);
401 temp
->status
= SS_IDLE
;
404 esocket_write_line(newsocket
, "nterfacer " PROTOCOL_VERSION
);
407 void derive_key(unsigned char *out
, char *password
, char *segment
, unsigned char *noncea
, unsigned char *nonceb
, unsigned char *extra
, int extralen
) {
410 SHA256_Update(&c
, (unsigned char *)password
, strlen(password
));
411 SHA256_Update(&c
, (unsigned char *)":", 1);
412 SHA256_Update(&c
, (unsigned char *)segment
, strlen(segment
));
413 SHA256_Update(&c
, (unsigned char *)":", 1);
414 SHA256_Update(&c
, noncea
, 16);
415 SHA256_Update(&c
, (unsigned char *)":", 1);
416 SHA256_Update(&c
, nonceb
, 16);
417 SHA256_Update(&c
, (unsigned char *)":", 1);
418 SHA256_Update(&c
, extra
, extralen
);
419 SHA256_Final(out
, &c
);
422 SHA256_Update(&c
, out
, 32);
423 SHA256_Final(out
, &c
);
426 int nterfacer_line_event(struct esocket
*sock
, char *newline
) {
427 struct sconnect
*socket
= sock
->tag
;
428 char *response
, *theirnonceh
= NULL
, *theirivh
= NULL
;
429 unsigned char theirnonce
[16], theiriv
[16];
432 switch(socket
->status
) {
434 if(strcasecmp(newline
, ANTI_FULL_VERSION
)) {
435 nterface_log(nrl
, NL_INFO
, "Protocol mismatch from %s: %s", socket
->permit
->hostname
->content
, newline
);
438 unsigned char challenge
[32];
439 char ivhex
[16 * 2 + 1], noncehex
[16 * 2 + 1];
441 if(!get_entropy(challenge
, 32) || !get_entropy(socket
->iv
, 16)) {
442 nterface_log(nrl
, NL_ERROR
, "Unable to open challenge/IV entropy bin!");
446 int_to_hex(challenge
, socket
->challenge
, 32);
447 int_to_hex(socket
->iv
, ivhex
, 16);
449 memcpy(socket
->response
, challenge_response(socket
->challenge
, socket
->permit
->password
->content
), sizeof(socket
->response
));
450 socket
->response
[sizeof(socket
->response
) - 1] = '\0'; /* just in case */
452 socket
->status
= SS_VERSIONED
;
453 if(!generate_nonce(socket
->ournonce
, 1)) {
454 nterface_log(nrl
, NL_ERROR
, "Unable to generate nonce!");
457 int_to_hex(socket
->ournonce
, noncehex
, 16);
459 if(esocket_write_line(sock
, "%s %s %s", socket
->challenge
, ivhex
, noncehex
))
465 for(response
=newline
;*response
;response
++) {
466 if((*response
== ' ') && (*(response
+ 1))) {
468 theirivh
= response
+ 1;
474 for(response
=theirivh
;*response
;response
++) {
475 if((*response
== ' ') && (*(response
+ 1))) {
477 theirnonceh
= response
+ 1;
483 if(!theirivh
|| (strlen(theirivh
) != 32) || !hex_to_int(theirivh
, theiriv
, sizeof(theiriv
)) ||
484 !theirnonceh
|| (strlen(theirnonceh
) != 32) || !hex_to_int(theirnonceh
, theirnonce
, sizeof(theirnonce
))) {
485 nterface_log(nrl
, NL_INFO
, "Protocol error drop: %s", socket
->permit
->hostname
->content
);
489 if(!memcmp(socket
->ournonce
, theirnonce
, sizeof(theirnonce
))) {
490 nterface_log(nrl
, NL_INFO
, "Bad nonce drop: %s", socket
->permit
->hostname
->content
);
494 if(!strncasecmp(newline
, socket
->response
, sizeof(socket
->response
))) {
495 unsigned char theirkey
[32], ourkey
[32];
497 derive_key(ourkey
, socket
->permit
->password
->content
, socket
->challenge
, socket
->ournonce
, theirnonce
, (unsigned char *)"SERVER", 6);
499 derive_key(theirkey
, socket
->permit
->password
->content
, socket
->response
, theirnonce
, socket
->ournonce
, (unsigned char *)"CLIENT", 6);
500 nterface_log(nrl
, NL_INFO
, "Authed: %s", socket
->permit
->hostname
->content
);
501 socket
->status
= SS_AUTHENTICATED
;
502 switch_buffer_mode(sock
, ourkey
, socket
->iv
, theirkey
, theiriv
);
504 if(esocket_write_line(sock
, "Oauth"))
507 nterface_log(nrl
, NL_INFO
, "Bad CR drop: %s", socket
->permit
->hostname
->content
);
512 case SS_AUTHENTICATED
:
513 nterface_log(nrl
, NL_INFO
|NL_LOG_ONLY
, "L(%s): %s", socket
->permit
->hostname
->content
, newline
);
514 reason
= nterfacer_new_rline(newline
, sock
, &number
);
516 if(reason
== RE_SOCKET_ERROR
)
518 if(reason
!= RE_BAD_LINE
) {
519 if(esocket_write_line(sock
, "%d,E%d,%s", number
, reason
, request_error(reason
)))
532 int nterfacer_new_rline(char *line
, struct esocket
*socket
, int *number
) {
533 char *sp
, *p
, *parsebuf
= NULL
, *pp
, commandbuf
[MAX_BUFSIZE
], *args
[MAX_ARGS
], *newp
;
535 struct service_node
*service
;
536 struct rline
*prequest
;
540 if(!line
|| !line
[0] || (line
[0] == ','))
543 for(sp
=line
;*sp
;sp
++)
547 if(!*sp
|| !*(sp
+ 1))
552 for(service
=tree
;service
;service
=service
->next
)
553 if(!strcmp(service
->name
->content
, line
))
564 *number
= positive_atoi(sp
+ 1);
566 if((*number
< 1) || (*number
> INT32_MAX
))
570 nterface_log(nrl
, NL_DEBUG
, "Unable to find service: %s", line
);
571 return RE_SERVICER_NOT_FOUND
;
576 for(pp
=p
+1;*pp
;pp
++) {
577 if((*pp
== '\\') && *(pp
+ 1)) {
578 if(*(pp
+ 1) == ',') {
580 } else if(*(pp
+ 1) == '\\') {
584 } else if(*pp
== ',') {
591 if(*pp
== '\0') { /* if we're at the end already, we have no arguments */
594 argcount
= 1; /* we have a comma, so we have one already */
599 for(hl
=service
->handlers
;hl
;hl
=hl
->next
)
600 if(!strncmp(hl
->command
->content
, commandbuf
, sizeof(commandbuf
)))
604 return RE_COMMAND_NOT_FOUND
;
607 parsebuf
= (char *)ntmalloc(strlen(pp
) + 1);
608 MemCheckR(parsebuf
, RE_MEM_ERROR
);
610 for(newp
=args
[0]=parsebuf
,pp
++;*pp
;pp
++) {
611 if((*pp
== '\\') && *(pp
+ 1)) {
612 if(*(pp
+ 1) == ',') {
614 } else if(*(pp
+ 1) == '\\') {
618 } else if(*pp
== ',') {
620 args
[argcount
++] = newp
;
621 if(argcount
> MAX_ARGS
) {
623 return RE_TOO_MANY_ARGS
;
631 if(argcount
< hl
->args
) {
632 if(argcount
&& parsebuf
)
634 return RE_WRONG_ARG_COUNT
;
637 prequest
= (struct rline
*)ntmalloc(sizeof(struct rline
));
640 if(argcount
&& parsebuf
)
645 prequest
->service
= service
;
646 prequest
->handler
= hl
;
647 prequest
->buf
[0] = '\0';
648 prequest
->curpos
= prequest
->buf
;
649 prequest
->tag
= NULL
;
650 prequest
->id
= *number
;
651 prequest
->next
= rlines
;
652 prequest
->socket
= socket
;
653 prequest
->callback
= NULL
;
656 re
= (hl
->function
)(prequest
, argcount
, args
);
658 if(argcount
&& parsebuf
)
664 void nterfacer_disconnect_event(struct esocket
*sock
) {
665 struct sconnect
*socket
= sock
->tag
;
669 nterface_log(nrl
, NL_INFO
, "Disconnected from %s.", socket
->permit
->hostname
->content
);
672 for(li
=rlines
;li
;li
=li
->next
)
673 if(li
->socket
&& (li
->socket
->tag
== socket
))
679 int ri_append(struct rline
*li
, char *format
, ...) {
680 char buf
[MAX_BUFSIZE
], escapedbuf
[MAX_BUFSIZE
* 2 + 1], *p
, *tp
;
681 int sizeleft
= sizeof(li
->buf
) - (li
->curpos
- li
->buf
);
684 va_start(ap
, format
);
686 if(vsnprintf(buf
, sizeof(buf
), format
, ap
) >= sizeof(buf
)) {
693 for(tp
=escapedbuf
,p
=buf
;*p
||(*tp
='\0');*tp
++=*p
++)
694 if((*p
== ',') || (*p
== '\\'))
698 if(li
->curpos
== li
->buf
) {
699 li
->curpos
+=snprintf(li
->curpos
, sizeleft
, "%s", escapedbuf
);
701 li
->curpos
+=snprintf(li
->curpos
, sizeleft
, ",%s", escapedbuf
);
705 if(sizeof(li
->buf
) - (li
->curpos
- li
->buf
) > 0) {
712 int ri_error(struct rline
*li
, int error_code
, char *format
, ...) {
713 char buf
[MAX_BUFSIZE
], escapedbuf
[MAX_BUFSIZE
* 2 + 1], *p
, *tp
;
714 struct rline
*pp
, *lp
= NULL
;
718 if(li
->socket
|| li
->callback
) {
719 va_start(ap
, format
);
720 vsnprintf(buf
, sizeof(buf
), format
, ap
);
723 for(tp
=escapedbuf
,p
=buf
;*p
||(*tp
='\0');*tp
++=*p
++)
724 if((*p
== ',') || (*p
== '\\'))
727 if(esocket_write_line(li
->socket
, "%d,OE%d,%s", li
->id
, error_code
, escapedbuf
))
728 retval
= RE_SOCKET_ERROR
;
730 if(error_code
== 0) /* :P */
733 nterfacer_sendcallback(li
, error_code
, escapedbuf
);
737 for(pp
=rlines
;pp
;lp
=pp
,pp
=pp
->next
) {
752 int ri_final(struct rline
*li
) {
753 struct rline
*pp
, *lp
= NULL
;
757 if(esocket_write_line(li
->socket
, "%d,OO%s", li
->id
, li
->buf
))
758 retval
= RE_SOCKET_ERROR
;
759 } else if(li
->callback
) {
760 nterfacer_sendcallback(li
, 0, li
->buf
);
763 for(pp
=rlines
;pp
;lp
=pp
,pp
=pp
->next
) {
778 int ping_handler(struct rline
*ri
, int argc
, char **argv
) {
791 static const int XMAXARGS
= 50;
793 static void execrline(void *arg
) {
794 struct sched_rline
*sr
= arg
;
796 char *argv
[XMAXARGS
], *buf
;
801 for(i
=0;i
<sr
->argc
;i
++) {
803 buf
+=strlen(buf
) + 1;
806 re
= (sr
->hl
->function
)(&sr
->rl
, sr
->argc
, argv
);
809 Error("nterfacer", ERR_WARNING
, "sendline: error occured calling %p %d: %s", sr
->hl
->function
, re
, request_error(re
));
812 void *nterfacer_sendline(char *service
, char *command
, int argc
, char **argv
, rline_callback callback
, void *tag
) {
813 struct service_node
*servicep
;
814 struct rline
*prequest
;
815 struct sched_rline
*sr
;
820 for(servicep
=tree
;servicep
;servicep
=servicep
->next
)
821 if(!strcmp(servicep
->name
->content
, service
))
825 Error("nterfacer", ERR_STOP
, "Over maximum arguments.");
828 Error("nterfacer", ERR_WARNING
, "sendline: service not found: %s", service
);
832 for(hl
=servicep
->handlers
;hl
;hl
=hl
->next
)
833 if(!strcmp(hl
->command
->content
, command
))
837 Error("nterfacer", ERR_WARNING
, "sendline: command not found: %s", command
);
841 if(argc
< hl
->args
) {
842 Error("nterfacer", ERR_WARNING
, "sendline: wrong number of arguments: %s", command
);
846 /* we have to create a copy of the arguments for reentrancy reasons, grr */
849 totallen
+=strlen(argv
[i
]) + 1;
851 /* HACKY but allows existing code to still work */
852 sr
= (struct sched_rline
*)ntmalloc(sizeof(struct sched_rline
) + totallen
);
861 for(i
=0;i
<argc
;i
++) {
862 size_t len
= strlen(argv
[i
]) + 1;
863 memcpy(buf
, argv
[i
], len
);
868 prequest
->service
= servicep
;
869 prequest
->handler
= hl
;
870 prequest
->buf
[0] = '\0';
871 prequest
->curpos
= prequest
->buf
;
874 prequest
->socket
= NULL
;
875 prequest
->callback
= callback
;
877 prequest
->next
= rlines
;
880 scheduleoneshot(time(NULL
), execrline
, sr
);
885 void nterfacer_freeline(void *tag
) {
886 struct sched_rline
*prequest
= tag
;
888 prequest
->rl
.callback
= NULL
;
889 if(prequest
->schedule
)
890 deleteschedule(prequest
->schedule
, execrline
, NULL
);
893 #define MAX_LINES 8192
896 static void nterfacer_sendcallback(struct rline
*ri
, int error
, char *buf
) {
897 char *lines
[MAX_LINES
+1];
898 char newbuf
[MAX_BUFSIZE
+5];
899 char *s
, *d
, *laststart
;
902 for(s
=buf
,laststart
=d
=newbuf
;*s
;s
++) {
903 if((*s
== '\\') && *(s
+ 1)) {
904 if(*(s
+ 1) == ',') {
906 } else if(*(s
+ 1) == '\\') {
910 } else if(*s
== ',') {
912 if(linec
>= MAX_LINES
- 5) {
913 nterfacer_sendcallback(ri
, BF_OVER
, "Buffer overflow.");
917 lines
[linec
++] = laststart
;
924 lines
[linec
++] = laststart
;
926 ri
->callback(error
, linec
, lines
, ri
->tag
);