/* Copyright (c) 2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file useraudi.c Useraudi main module. */ /** In the useraudi module. */ #define USERAUDI_C 1 #include "useraudi.h" $(trace-include) /** Name of socket to connect to backend daemon. */ static char *sockname = NULL; /** Storage of allowed IP addresses. */ static dk_storage_t *s_allow = NULL; /** Iterator for storage of allowed IP addresses. */ static dk_storage_iterator_t *i_allow = NULL; /** Input line buffer. */ static char il[USERAUD_LINESIZE]; /** Output line buffer. */ static char ol[USERAUD_LINESIZE]; /** Socket. */ static char *cmd_socket[] = { "socket", NULL }; /** Allow. */ static char *cmd_allow[] = { "allow", NULL }; /** Network user name test. */ static char *cmd_net_user_name_test[] = { "net", "user", "name", "test", NULL }; /** Configuration options of interest for useraudi. */ static char **cmds[] = { /* 0 */ cmd_socket, /* 1 */ cmd_allow, /* 2 */ cmd_net_user_name_test, NULL }; /** Flag: Allow user name tests. */ static int allow_username_test = 0; /** Flag: A user name test was attempted. */ static int attempt_username_test = 0; /** Analyze whether or not the text \a t has the given number \a n of strings. @param n Number of strings to find. @param t Text to split. @return Flag: Number matches. */ static int text_parts DK_P2(int,n, char *,t) { int back = 0; int i = 0; char *p; p = dkstr_start(t, NULL); while(p) { i++; p = dkstr_next(p, NULL); } if(i == n) { back = 1; } return back; } /** Analyze whether or not to contact backend. @param opcode Request type. @param args Request argument. @return Flag whether or not to contact backend. */ static int analyze DK_P2(int,opcode, char *,args) { int back = 0; switch(opcode) { case 100: case 101: { back = text_parts(2, args); } break; case 102: case 103: { back = text_parts(3, args); } break; case 104: case 105: { back = text_parts(2, args); } break; case 106: { back = text_parts(2, args); } break; case 107: case 108: { back = text_parts(4, args); } break; case 109: { back = text_parts(2, args); } break; case 110: case 111: { if(allow_username_test) { back = text_parts(1, args); } } break; } return back; } /** Run service loop. @param is_allowed Flag: Client is allowed to use service. */ static void run_service_loop DK_P1(int,is_allowed) { char *p1; /* Start of input line. */ char *p2; /* Start of arguments. */ char *l; int cc = 1; int lineok; int opcode; USERAUD_CONNECTION *uc = NULL; USERAUD_RESPONSE *ur = NULL; if(is_allowed) { uc = uacl_open(sockname); } while(cc) { cc = 0; attempt_username_test = 0; if(fgets(il, sizeof(il), stdin)) { cc = 1; lineok = 0; p1 = dkstr_start(il, NULL); if(p1) { dkstr_chomp(p1, NULL); strcpy(ol, p1); strcpy(il, ol); p1 = il; p2 = dkstr_next(ol, NULL); if(sscanf(p1, "%d", &opcode) == 1) { lineok = analyze(opcode, p2); } } if(lineok) { if(is_allowed) { /* Contact backend server. */ if(uc) { ur = uacl_process(uc, p1); if(ur) { uacl_response_reset(ur); while((l = uacl_response_line(ur))) { printf("%s\n", l); fflush(stdout); } uacl_response_close(ur); ur = NULL; } else { /* printf("409\n"); fflush(stdout); */ switch(uc->ec) { case UA_ERROR_MEMORY: { printf("409\n"); fflush(stdout); } break; case UA_ERROR_NO_RESPONSE: case UA_ERROR_SOCKET_FAILED: case UA_ERROR_CONNECT_FAILED: { printf("400\n"); fflush(stdout); } break; case UA_ERROR_ILLEGAL_ARGUMENTS: case UA_ERROR_SOCKET_NAME_TOO_LONG: { printf("410\n"); fflush(stdout); } break; } } } else { printf("409\n"); fflush(stdout); } } else { /* Send failure code. */ switch(opcode) { case 100: case 101: case 102: case 103: { printf("401\n"); fflush(stdout); } break; case 104: case 105: { printf("403\n"); fflush(stdout); } break; case 106: { printf("406\n"); fflush(stdout); } break; case 107: case 108: { printf("407\n"); fflush(stdout); } break; case 109: { printf("200\n"); } break; } } } else { if(attempt_username_test && (!allow_username_test)) { printf("400\n"); fflush(stdout); } else { printf("411\n"); fflush(stdout); } } } } if(uc) { uacl_close(uc); } } /** Run a session. Read request (one input line), check request, forward it to backend daemon, receive response, send response back. */ static void run_session DK_P0() { int is_allowed = 1; /* Flag: Peer is allowed to connect. */ struct sockaddr_in sin; socklen_t st; UAPEER *p; unsigned long ip; $? "+ run_session" st = sizeof(sin); if((s_allow) && (i_allow)) { if(getpeername(0, (struct sockaddr *)(&sin), &st) == 0) { is_allowed = 0; ip = sin.sin_addr.s_addr; dksto_it_reset(i_allow); while(((p = (UAPEER *)dksto_it_next(i_allow)) != NULL) && (!is_allowed)) { if((ip & (p->mask)) == ((p->ip) & (p->mask))) { is_allowed = 1; $? ". peer IP address is listed" } } } } if(is_allowed) { run_service_loop(is_allowed); } $? "- run_session" } /** Read configuration. @param fn Configuration file name. @return 1 on success, 0 on error. */ static int read_conf DK_P1(char *,fn) { int back = 1; int active_section = 0; unsigned long ip, ma; FILE *fipo; char *p1, *p2, *p3; UAPEER *uap; char *parts[16]; size_t nparts = 0; int ac = 0; $? "+ read_conf" fipo = fopen(fn, "r"); if(fipo) { $? ". file opened" while((back) && fgets(il, sizeof(il), fipo)) { p1 = dkstr_start(il, NULL); if(p1) { dkstr_chomp(p1, NULL); $? ". line=\"%s\"", p1 if(*p1 != '#') { if(*p1 == '[') { active_section = 0; p1++; p1 = dkstr_start(p1, NULL); if(p1) { p2 = dkstr_chr(p1, ']'); if(p2) { *p2 = '\0'; dkstr_chomp(p1, NULL); if(strcmp(p1, "options") == 0) { active_section = 1; } } else { back = 0; } } else { back= 0; } $? ". active_section=%d", active_section } else { if(active_section) { $? ". in active section" p2 = dkstr_chr(p1, '='); if(p2) { *(p2++) = '\0'; p2 = dkstr_start(p2, NULL); if(p2) { dkstr_chomp(p1, NULL); dkstr_chomp(p2, NULL); nparts = dkstr_explode(parts, 15, p1, NULL); if(nparts > 0) { ac = dkstr_find_multi_part_cmd(parts,cmds,0); switch(ac) { case 0: { // socket if(sockname == NULL) { sockname = dkstr_dup(p2); if(!(sockname)) { $? "! memory" back = 0; } $? ". socket=\"%s\"", TR_STR(sockname) } else { $? "! socket already defined" back = 0; } } break; case 1: { // allow if(!(s_allow)) { s_allow = dksto_open(0); if(s_allow) { i_allow = dksto_it_open(s_allow); if(!(i_allow)) { $? "! memory" dksto_close(s_allow); s_allow = NULL; back = 0; } } else { $? "! memory" back = 0; } } if((s_allow) && (i_allow)) { p3 = dkstr_chr(p2, '/'); if(p3) { $? ". IP/mask" *(p3++) = '\0'; p3 = dkstr_start(p3, NULL); ip = uatcs_dotted_string_to_ip(p2); ma = 0xFFFFFFFFUL; if(p3) { ma = uatcs_dotted_string_to_ip(p3); } } else { $? ". IP only" ip = uatcs_dotted_string_to_ip(p2); ma = 0xFFFFFFFFUL; } uap = dk_new(UAPEER,1); if(uap) { uap->ip = htonl(ip); uap->mask = htonl(ma); if(!dksto_add(s_allow, (void *)uap)) { dk_delete(uap); $? "! memory" back = 0; } } else { $? "! memory" back = 0; } } else { $? "! memory" back = 0; } } break; case 2: { // net user name test if(dkstr_is_bool(p2)) { allow_username_test = (dkstr_is_on(p2) ? 1 : 0); } else { allow_username_test = 0; } } break; } } else { back = 0; } } else { back = 0; } } else { back = 0; } } } } } } fclose(fipo); } else { $? "! failed to open conf" back = 0; } $? "- read_conf %d", back return back; } /** Clean up. Release dynamically allocated memory. */ static void cleanup DK_P0() { UAPEER *p; if(s_allow) { if(i_allow) { dksto_it_reset(i_allow); while((p = (UAPEER *)dksto_it_next(i_allow)) != NULL) { dk_delete(p); } dksto_it_close(i_allow); } dksto_close(s_allow); } s_allow = NULL; i_allow = NULL; if(sockname) { dk_delete(sockname); sockname = NULL; } } /** The main function. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { char *cfgname; $(trace-init /tmp/useraudi.deb) $? "+ main" if(argc > 1) { cfgname = argv[1]; } else { cfgname = uatcs_get_default_config_file_name(); } if(read_conf(cfgname)) { run_session(); } cleanup(); $? "- main" $(trace-end) exit(0); return 0; }