📄 mio.c
字号:
/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*//** * @file mio.c * @brief MIO -- Managed Input/Output * * The purpose of this file, is mainly to provide support, to any component * of jabberd, for abstraced I/O functions. This works much like tstreams, * and will incorporate the functionality of io_select initially, but will be * expanded to support any socket handling model, such as polld, SIGIO, etc * * This works to abstract the socket work, and hide it from the component, * this way, the component does not have to deal with any complexeties of * socket functions. */#define MIO#include <jabberd.h>#include <errno.h>/******************************************************** ************* Internal MIO Functions ***************** ********************************************************//** * @brief structure that holds the global mio data */typedef struct mio_main_st{ pool p; /**< (memory-)pool to hold this data */ mio master__list; /**< a list of all the sockets */ pth_t t; /**< a pointer to thread for signaling */ int shutdown; /**< flag that the select loop can be left (if value is 1) */ int zzz[2]; /**< pipe used to send signals to the select loop */ int zzz_active; /**< if set to something else then 1, there has been sent a signal already, that is not yet processed */ struct karma *k; /**< default karma */ int rate_t, rate_p; /**< default rate, if any */} _ios,*ios;/** * @brief internal structure holding data of the destination where we connect to */typedef struct mio_connect_st{ pool p; /**< (memory-)pool to hold this data */ char *ip; /**< IP address where to connect to */ int port; /**< port where to connect to */ void *cb; /**< callback function that should be notified on the new connection */ void *cb_arg; /**< argument that should be passed to the callback function */ mio_connect_func cf; mio_handlers mh; pth_t t; /**< thread for this connection */ int connected; /**< flag if the socket is connected */} _connect_data, *connect_data;/* global object */int mio__errno = 0; /**< mio_ssl.c passes EAGAIN on this variable, if a read/write would have blocked */int mio__ssl_reread = 0;/**< mio_ssl.c tells us on this variable, that there might be more to read */ios mio__data = NULL; /**< global data for mio */char *mio__bounce_uri = NULL; /**< where to bounce HTTP requests to */extern xmlnode greymatter__;#ifdef WITH_IPV6/** * compare two IPv6 or IPv4 addresses if they are in the same network * * @param addr1 the first address * @param addr2 the second address * @param netsize how many bits are in the network address * @return 1 if both addresses are in the same network, 0 if not */int _mio_compare_ipv6(const struct in6_addr *addr1, const struct in6_addr *addr2, int netsize){ int i; u_int8_t mask; if(netsize > 128) netsize = 128; for(i = 0; i < netsize/8; i++) { if(addr1->s6_addr[i] != addr2->s6_addr[i]) return 0; } if (netsize%8 == 0) return 1; mask = 0xff << (8 - netsize%8); return ((addr1->s6_addr[i]&mask) == (addr2->s6_addr[i]&mask));}/** * convert a netmask to an IPv6 network size * * If the netmask is NULL, 128 is returned. * * E.g. 255.255.255.0 is converted to 120 (the network is ::ffff:a.b.c.0 in this case) * * @param netmask string containing the netmask in traditional IPv4 notation * @return number of bits in the network part of the address (range 96...128, because the argument is IPv4) */int _mio_netmask_to_ipv6(const char *netmask){ struct in_addr addr; if (netmask == NULL) { return 128; } if (inet_pton(AF_INET, netmask, &addr)) { uint32_t temp = ntohl(addr.s_addr); int netmask = 128; while (netmask>96 && temp%2==0) { netmask--; temp /= 2; } return netmask; } return atoi(netmask);}#endif/** * check if an IP address (IPv4 or IPv6) is allowed to connect * * @param address the address that should be checked * @return 1 if allowed by default, or IP inside an allowed network, 2 if explicitly allowed IP address, 0 if not allowed */int _mio_allow_check(const char *address){#ifdef WITH_IPV6 char temp_address[INET6_ADDRSTRLEN]; char temp_ip[INET6_ADDRSTRLEN]; static struct in_addr tmpa;#endif xmlnode io = xmlnode_get_tag(greymatter__, "io"); xmlnode cur;#ifdef WITH_IPV6 if (inet_pton(AF_INET, address, &tmpa)) { strcpy(temp_address, "::ffff:"); strcat(temp_address, address); address = temp_address; }#endif if(xmlnode_get_tag(io, "allow") == NULL) return 1; /* if there is no allow section, allow all */ for(cur = xmlnode_get_firstchild(io); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { char *ip, *netmask;#ifdef WITH_IPV6 struct in6_addr in_address, in_ip; int in_netmask;#else struct in_addr in_address, in_ip, in_netmask;#endif if(xmlnode_get_type(cur) != NTYPE_TAG) continue; if(j_strcmp(xmlnode_get_name(cur), "allow") != 0) continue; ip = xmlnode_get_tag_data(cur, "ip"); netmask = xmlnode_get_tag_data(cur, "mask"); if(ip == NULL) continue;#ifdef WITH_IPV6 if (inet_pton(AF_INET, ip, &tmpa)) { strcpy(temp_ip, "::ffff:"); strcat(temp_ip, ip); ip = temp_ip; } inet_pton(AF_INET6, address, &in_address);#else inet_aton(address, &in_address);#endif if(ip != NULL)#ifdef WITH_IPV6 inet_pton(AF_INET6, ip, &in_ip);#else inet_aton(ip, &in_ip);#endif if(netmask != NULL) {#ifdef WITH_IPV6 in_netmask = _mio_netmask_to_ipv6(netmask); if(_mio_compare_ipv6(&in_address, &in_ip, in_netmask))#else inet_aton(netmask, &in_netmask); if((in_address.s_addr & in_netmask.s_addr) == (in_ip.s_addr & in_netmask.s_addr))#endif { /* this ip is in the allow network */ return 1; } } else {#ifdef WITH_IPV6 if(_mio_compare_ipv6(&in_ip, &in_address, 128))#else if(in_ip.s_addr == in_address.s_addr)#endif return 2; /* exact matches hold greater weight */ } } /* deny the rest */ return 0;}/** * check if an IP address (IPv4 or IPv6) is forbitten to connect * * @param address the address that should be checked * @return 1 if forbidden by default, or IP inside an forbidden network, 2 if explicitly forbidden IP address, 0 if not allowed */int _mio_deny_check(const char *address){#ifdef WITH_IPV6 char temp_address[INET6_ADDRSTRLEN]; char temp_ip[INET6_ADDRSTRLEN]; static struct in_addr tmpa;#endif xmlnode io = xmlnode_get_tag(greymatter__, "io"); xmlnode cur;#ifdef WITH_IPV6 if (inet_pton(AF_INET, address, &tmpa)) { strcpy(temp_address, "::ffff:"); strcat(temp_address, address); address = temp_address; }#endif if(xmlnode_get_tag(io, "deny") == NULL) return 0; /* if there is no deny section, allow all */ for(cur = xmlnode_get_firstchild(io); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { char *ip, *netmask;#ifdef WITH_IPV6 struct in6_addr in_address, in_ip; int in_netmask;#else struct in_addr in_address, in_ip, in_netmask;#endif if(xmlnode_get_type(cur) != NTYPE_TAG) continue; if(j_strcmp(xmlnode_get_name(cur), "deny") != 0) continue; ip = xmlnode_get_tag_data(cur, "ip"); netmask = xmlnode_get_tag_data(cur, "mask"); if(ip == NULL) continue;#ifdef WITH_IPV6 if (inet_pton(AF_INET, ip, &tmpa)) { strcpy(temp_ip, ":ffff:"); strcat(temp_ip, ip); ip = temp_ip; } inet_pton(AF_INET6, address, &in_address);#else inet_aton(address, &in_address);#endif if(ip != NULL)#ifdef WITH_IPV6 inet_pton(AF_INET6, ip, &in_ip);#else inet_aton(ip, &in_ip);#endif if(netmask != NULL) {#ifdef WITH_IPV6 in_netmask = _mio_netmask_to_ipv6(netmask); if (_mio_compare_ipv6(&in_address, &in_ip, in_netmask))#else inet_aton(netmask, &in_netmask); if((in_address.s_addr & in_netmask.s_addr) == (in_ip.s_addr & in_netmask.s_addr))#endif { /* this ip is in the deny network */ return 1; } } else {#ifdef WITH_IPV6 if(_mio_compare_ipv6(&in_ip, &in_address, 128))#else if(in_ip.s_addr == in_address.s_addr)#endif return 2; /* must be an exact match, if no netmask */ } } return 0;}/** * callback for Heartbeat, increments karma, and signals the * select loop, whenever a socket's punishment is over * * @param arg unused/ignored * @return always r_DONE */result _karma_heartbeat(void*arg){ mio cur; /* if there is nothing to do, just return */ if(mio__data == NULL || mio__data->master__list == NULL) return r_DONE; /* loop through the list, and add karma where appropriate */ for(cur = mio__data->master__list; cur != NULL; cur = cur->next) { if(cur->k.dec != 0) { /* Karma is enabled for this connection */ int was_negative = 0; /* don't update if we are closing, or pre-initilized */ if(cur->state == state_CLOSE || cur->k.init == 0) continue; /* if we are being punished, set the flag */ if(cur->k.val < 0) was_negative = 1; /* possibly increment the karma */ karma_increment( &cur->k ); /* punishment is over */ if(was_negative && cur->k.val >= 0) { log_debug2(ZONE, LOGT_IO, "Punishment Over for socket %d: ", cur->fd); /* we don't have to signal again, if a signal is pending */ if (mio__data->zzz_active <= 0) { mio__data->zzz_active++; pth_write(mio__data->zzz[1]," ",1); } } } } /* always return r_DONE, to keep getting heartbeats */ return r_DONE;}/** * unlinks a socket from the master list * * @param m socket that should be unlinked from mio__data->master__list */void _mio_unlink(mio m){ if(mio__data == NULL) return; if(mio__data->master__list == m) mio__data->master__list = mio__data->master__list->next; if(m->prev != NULL) m->prev->next = m->next; if(m->next != NULL) m->next->prev = m->prev;}/** * links a socket to the master list * * The new socket is inserted as the first list element, but you must not rely on this. * * @param m socket that should be linked to mio__data->master__list */void _mio_link(mio m){ if(mio__data == NULL) return; m->next = mio__data->master__list; m->prev = NULL; if(mio__data->master__list != NULL) mio__data->master__list->prev = m; mio__data->master__list = m;}/** * Dump this socket's write queue. * * Tries to write * as much of the write queue as it can, before the * write call would block the server * * @param m the connection that should get it's write queue dumped * @return -1 on error, 0 on success, and 1 if more data to write */int _mio_write_dump(mio m){ int len; mio_wbq cur; /* try to write as much as we can */ while(m->queue != NULL) { cur = m->queue; log_debug2(ZONE, LOGT_IO, "write_dump writing data: %.*s", cur->len, cur->cur); /* write a bit from the current buffer */ len = (*m->mh->write)(m, cur->cur, cur->len); /* we had an error on the write */ if(len == 0) { if(m->cb != NULL) (*(mio_std_cb)m->cb)(m, MIO_ERROR, m->cb_arg); return -1; } if(len < 0) { /* if we have an error, that isn't a blocking issue */ if(errno != EWOULDBLOCK && errno != EINTR && errno != EAGAIN && mio__errno != EAGAIN) { /* bounce the queue */ if(m->cb != NULL) (*(mio_std_cb)m->cb)(m, MIO_ERROR, m->cb_arg); return -1; } return 1; } /* we didnt' write it all, move the current buffer up */ else if(len < cur->len) { cur->cur += len; cur->len -= len; return 1; } /* we wrote the entire node, kill it and move on */ else { m->queue = m->queue->next; if(m->queue == NULL) m->tail = NULL; pool_free(cur->p); } } return 0;}/** * internal close function * * does a final write of the queue, bouncing and freeing all memory * * @param m the connection that gets closed */void _mio_close(mio m){ int ret = 0; xmlnode cur; /* ensure that the state is set to CLOSED */ m->state = state_CLOSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -