/* * Create a new text chunk, the input text * will be unescaped first. */ struct text_chunk* new_chunk_unescape(str* src) { struct text_chunk* l; if (!src) return 0; l = ctl_malloc(sizeof(struct text_chunk)); if (!l) { ERR("No Memory Left\n"); return 0; } l->s.s = ctl_malloc(src->len + 1); if (!l->s.s) { ERR("No Memory Left\n"); ctl_free(l); return 0; } l->next = 0; l->flags = 0; if (unescape(&l->s, src->s, src->len) < 0) { ctl_free(l->s.s); ctl_free(l); return 0; } l->s.s[l->s.len] = '\0'; return l; }
/* * Remove directory path from filename and replace it * with the path configured through a module parameter. * * The result is allocated using ctl_malloc and thus * has to be freed using ctl_free */ static char *trim_filename(char * file) { int prefix_len, fn_len; char *new_fn; /* we only allow files in "/tmp" -- any directory * changes are not welcome */ if (strchr(file, '.') || strchr(file, '/') || strchr(file, '\\')) { ERR("Forbidden filename: %s\n" , file); return 0; } prefix_len = strlen(fifo_dir); fn_len = strlen(file); new_fn = ctl_malloc(prefix_len + fn_len + 1); if (new_fn == 0) { ERR("No memory left\n"); return 0; } memcpy(new_fn, fifo_dir, prefix_len); memcpy(new_fn + prefix_len, file, fn_len); new_fn[prefix_len + fn_len] = 0; return new_fn; }
/* * Create a new text chunk, the input text * will not be escaped. The function returns * 0 on an error */ struct text_chunk* new_chunk(str* src) { struct text_chunk* l; if (!src) return 0; l = ctl_malloc(sizeof(struct text_chunk)); if (!l) { ERR("No Memory Left\n"); return 0; } l->s.s = ctl_malloc(src->len + 1); if (!l->s.s) { ERR("No Memory Left\n"); ctl_free(l); return 0; } l->next = 0; l->flags = 0; memcpy(l->s.s, src->s, src->len); l->s.len = src->len; l->s.s[l->s.len] = '\0'; return l; }
static struct rpc_struct_l* new_rpc_struct() { struct rpc_struct_l* rs; /* alloc everything in one chunk */ rs=ctl_malloc(sizeof(struct rpc_struct_l)+STRUCT_MAX_BODY); if (rs==0) goto error; memset(rs, 0, sizeof(struct rpc_struct_l)); clist_init(&rs->substructs, next, prev); if (binrpc_init_pkt(&rs->pkt, (unsigned char*)rs+sizeof(struct rpc_struct_l), STRUCT_MAX_BODY)<0){ ctl_free(rs); goto error; } return rs; error: return 0; }
/** mark a pointer for freeing when the ctx is destroyed. * @return 0 on success, -1 on error */ inline static int binrpc_gc_track(struct binrpc_ctx* ctx, void* p) { struct binrpc_gc_block* b; int n; b=ctx->gc; if (b==0 || (b->idx>=b->p_no)){ n=(b==0)?BINRPC_GC_IBSIZE:b->p_no*2; b=ctl_malloc(sizeof(*b)+n*sizeof(void*)-sizeof(b->p)); if (b==0) return -1; b->p_no=n; b->idx=0; /* link in front */ b->next=ctx->gc; ctx->gc=b; } b->p[b->idx]=p; b->idx++; return 0; }
/* * Parse a structure */ static struct rpc_struct* new_struct(rpc_ctx_t* ctx, str* line) { char* comma, *colon; struct rpc_struct* s; str left, right = STR_NULL, name, value; struct text_chunk* n, *v; if (!line->len) { rpc_fault(ctx, 400, "Line %d Empty - Structure Expected", ctx->line_no); return 0; } s = (struct rpc_struct*)ctl_malloc(sizeof(struct rpc_struct)); if (!s) { rpc_fault(ctx, 500, "Internal Server Error (No Memory Left)"); return 0; } memset(s, 0, sizeof(struct rpc_struct)); s->ctx = ctx; left = *line; do { comma = q_memchr(left.s, ',', left.len); if (comma) { right.s = comma + 1; right.len = left.len - (comma - left.s) - 1; left.len = comma - left.s; } /* Split the record to name and value */ colon = q_memchr(left.s, ':', left.len); if (!colon) { rpc_fault(ctx, 400, "Colon missing in struct on line %d", ctx->line_no); goto err;; } name.s = left.s; name.len = colon - name.s; value.s = colon + 1; value.len = left.len - (colon - left.s) - 1; /* Create name chunk */ n = new_chunk_unescape(&name); if (!n) { rpc_fault(ctx, 400, "Error while processing struct member '%.*s' " "on line %d", name.len, ZSW(name.s), ctx->line_no); goto err; } n->next = s->names; s->names = n; /* Create value chunk */ v = new_chunk_unescape(&value); if (!v) { rpc_fault(ctx, 400, "Error while processing struct membeer '%.*s'" " on line %d", name.len, ZSW(name.s), ctx->line_no); goto err; } v->next = s->values; s->values = v; left = right; } while(comma); return s; err: if (s) free_struct(s); return 0; }
/* parses: * tcp|udp|unix:host_name:port * tcp|udp|unix:host_name * host_name:port * host_name * * * where host_name=string, ipv4 address, [ipv6 address], * unix socket path (starts with '/') */ struct id_list* parse_listen_id(char* l, int len, enum socket_protos def) { char* p; enum socket_protos proto; char* name; char* port_str; int port; int err; struct servent* se; char* s; struct id_list* id; s=ctl_malloc((len+1)*sizeof(char)); if (s==0){ LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n"); goto error; } memcpy(s, l, len); s[len]=0; /* null terminate */ /* duplicate */ proto=UNKNOWN_SOCK; port=0; name=0; port_str=0; p=s; if ((*p)=='[') goto ipv6; /* find proto or name */ for (; *p; p++){ if (*p==':'){ *p=0; if (strcasecmp("tcp", s)==0){ proto=TCP_SOCK; goto find_host; }else if (strcasecmp("udp", s)==0){ proto=UDP_SOCK; goto find_host; }else if (strcasecmp("unixd", s)==0){ proto=UNIXD_SOCK; goto find_host; }else if ((strcasecmp("unix", s)==0)||(strcasecmp("unixs", s)==0)){ proto=UNIXS_SOCK; goto find_host; #ifdef USE_FIFO }else if (strcasecmp("fifo", s)==0){ proto=FIFO_SOCK; goto find_host; #endif }else{ proto=UNKNOWN_SOCK; /* this might be the host */ name=s; goto find_port; } } } name=s; goto end; /* only name found */ find_host: p++; if (*p=='[') goto ipv6; name=p; for (; *p; p++){ if ((*p)==':'){ *p=0; goto find_port; } } goto end; /* nothing after name */ ipv6: name=p; p++; for(;*p;p++){ if(*p==']'){ if(*(p+1)==':'){ p++; *p=0; goto find_port; }else if (*(p+1)==0) goto end; }else{ goto error; } } find_port: p++; port_str=(*p)?p:0; end: /* fix all the stuff */ if (name==0) goto error; if (proto==UNKNOWN_SOCK){ /* try to guess */ if (port_str){ switch(def){ case TCP_SOCK: case UDP_SOCK: proto=def; break; default: proto=UDP_SOCK; DBG("guess:%s is a tcp socket\n", name); } }else if (name && strchr(name, '/')){ switch(def){ case TCP_SOCK: case UDP_SOCK: DBG("guess:%s is a unix socket\n", name); proto=UNIXS_SOCK; break; default: /* def is filename based => use default */ proto=def; } }else{ /* using default */ proto=def; } } if (port_str){ port=str2s(port_str, strlen(port_str), &err); if (err){ /* try getservbyname */ se=getservbyname(port_str, (proto==TCP_SOCK)?"tcp":(proto==UDP_SOCK)?"udp":0); if (se) port=ntohs(se->s_port); else goto error; } }else{ /* no port, check if the hostname is a port * (e.g. tcp:3012 == tcp:*:3012 */ if (proto==TCP_SOCK|| proto==UDP_SOCK){ port=str2s(name, strlen(name), &err); if (err){ port=0; }else{ name="*"; /* inaddr any */ } } } id=ctl_malloc(sizeof(struct id_list)); if (id==0){ LOG(L_ERR, "ERROR:parse_listen_id: out of memory\n"); goto error; } id->name=name; id->proto=proto; id->data_proto=P_BINRPC; id->port=port; id->buf=s; id->next=0; return id; error: if (s) ctl_free(s); return 0; }
int init_ctrl_sockets(struct ctrl_socket** c_lst, struct id_list* lst, int def_port, int perm, int uid, int gid) { struct id_list* l; int s; struct ctrl_socket* cs; int extra_fd; union sockaddr_u su; for (l=lst; l; l=l->next){ extra_fd=-1; switch(l->proto){ case UNIXS_SOCK: s=init_unix_sock(&su.sa_un, l->name, SOCK_STREAM, perm, uid, gid); break; case UNIXD_SOCK: s=init_unix_sock(&su.sa_un, l->name, SOCK_DGRAM, perm, uid, gid); break; case TCP_SOCK: if (l->port==0) l->port=def_port; s=init_tcpudp_sock(&su.sa_in, l->name, l->port, TCP_SOCK); break; case UDP_SOCK: if (l->port==0) l->port=def_port; s=init_tcpudp_sock(&su.sa_in, l->name, l->port, UDP_SOCK); break; #ifdef USE_FIFO case FIFO_SOCK: s=init_fifo_fd(l->name, perm, uid, gid, &extra_fd); break; #endif default: LOG(L_ERR, "ERROR: init_ctrl_listeners: unsupported" " proto %d\n", l->proto); continue; } if (s==-1) goto error; /* add listener */ cs=ctl_malloc(sizeof(struct ctrl_socket)); if (cs==0){ LOG(L_ERR, "ERROR: init_ctrl_listeners: out of memory\n"); goto error; } memset(cs,0, sizeof(struct ctrl_socket)); cs->transport=l->proto; cs->p_proto=l->data_proto; cs->fd=s; cs->write_fd=extra_fd; /* needed for fifo write */ cs->name=l->name; cs->port=l->port; cs->u=su; /* add it to the list */ cs->next=*c_lst; *c_lst=cs; } return 0; error: return -1; }