void t_network_address_set() { network_address *addr; addr = network_address_new(); g_assert_cmpint(network_address_set_address(addr, "127.0.0.1:3306"), ==, 0); g_assert_cmpint(network_address_set_address(addr, "127.0.0.1"), ==, 0); g_log_set_always_fatal(G_LOG_FATAL_MASK); /* shouldn't crash. * * we can't test if it works as we can't know if the host has setup IPv6 */ network_address_set_address(addr, "[::1]"); network_address_set_address(addr, "[::1]:3306"); /* should fail */ g_assert_cmpint(network_address_set_address(addr, "500.0.0.1"), ==, -1); g_assert_cmpint(network_address_set_address(addr, "127.0.0.1:"), ==, -1); g_assert_cmpint(network_address_set_address(addr, "[::1]:"), ==, -1); g_assert_cmpint(network_address_set_address(addr, "127.0.0.1:65536"), ==, -1); g_assert_cmpint(network_address_set_address(addr, "127.0.0.1:-1"), ==, -1); network_address_free(addr); }
/* * FIXME: 1) remove _set_address, make this function callable with result of same * 2) differentiate between reasons for "we didn't add" (now -1 in all cases) */ int network_backends_add(network_backends_t *bs, /* const */ gchar *address, backend_type_t type) { network_backend_t *new_backend; guint i; new_backend = network_backend_new(); new_backend->type = type; if (0 != network_address_set_address(new_backend->addr, address)) { network_backend_free(new_backend); return -1; } /* check if this backend is already known */ g_mutex_lock(bs->backends_mutex); for (i = 0; i < bs->backends->len; i++) { network_backend_t *old_backend = bs->backends->pdata[i]; if (strleq(S(old_backend->addr->name), S(new_backend->addr->name))) { network_backend_free(new_backend); g_mutex_unlock(bs->backends_mutex); g_critical("backend %s is already known!", address); return -1; } } g_ptr_array_add(bs->backends, new_backend); g_mutex_unlock(bs->backends_mutex); g_message("added %s backend: %s", (type == BACKEND_TYPE_RW) ? "read/write" : "read-only", address); return 0; }
static void t_network_address_tostring_ipv6() { #ifdef AF_INET6 network_address *addr; char buf[255]; gsize buf_len = sizeof(buf); GError *gerr = NULL; addr = network_address_new(); if (0 != network_address_set_address(addr, "[::1]")) { /* skip test, if resolving ::1 fails */ network_address_free(addr); return; } buf_len = sizeof(buf); /* should be large enough */ g_assert_cmpstr(network_address_tostring(addr, buf, &buf_len, NULL), ==, "::1"); g_assert_cmpint(3 + 1, ==, buf_len); buf_len = 3; /* too small */ g_assert(NULL == network_address_tostring(addr, buf, &buf_len, &gerr)); g_assert_cmpint(NETWORK_ADDRESS_ERROR, ==, gerr->domain); g_assert_cmpint(NETWORK_ADDRESS_ERROR_DST_TOO_SMALL, ==, gerr->code); g_clear_error(&gerr); network_address_free(addr); #endif }
/** * init the plugin with the parsed config */ static int network_mysqld_admin_plugin_apply_config(chassis *chas, chassis_plugin_config *config) { network_mysqld_con *con; network_socket *listen_sock; if (!config->address) config->address = g_strdup(":4041"); if (!config->admin_username) { g_critical("%s: --admin-username needs to be set", G_STRLOC); return -1; } if (!config->admin_password) { g_critical("%s: --admin-password needs to be set", G_STRLOC); return -1; } if (!config->lua_script) { g_critical("%s: --admin-lua-script needs to be set, <install-dir>/lib/mysql-proxy/lua/admin.lua may be a good value", G_STRLOC); return -1; } /** * create a connection handle for the listen socket */ con = network_mysqld_con_new(); network_mysqld_add_connection(chas, con); con->config = config; config->listen_con = con; listen_sock = network_socket_new(); con->server = listen_sock; /* set the plugin hooks as we want to apply them to the new connections too later */ network_mysqld_server_connection_init(con); /* FIXME: network_socket_set_address() */ if (0 != network_address_set_address(listen_sock->dst, config->address)) { return -1; } /* FIXME: network_socket_bind() */ if (0 != network_socket_bind(listen_sock)) { return -1; } /** * call network_mysqld_con_accept() with this connection when we are done */ event_set(&(listen_sock->event), listen_sock->fd, EV_READ|EV_PERSIST, network_mysqld_con_accept, con); event_base_set(chas->event_base, &(listen_sock->event)); event_add(&(listen_sock->event), NULL); return 0; }
/* * FIXME: 1) remove _set_address, make this function callable with result of same * 2) differentiate between reasons for "we didn't add" (now -1 in all cases) */ int network_backends_add(network_backends_t *bs, /* const */ gchar *address, backend_type_t type) { network_backend_t *new_backend; guint i; new_backend = network_backend_new(bs->event_thread_count); new_backend->type = type; gchar *p = NULL; if (type == BACKEND_TYPE_RO) { guint weight = 1; p = strrchr(address, '@'); if (p != NULL) { *p = '\0'; weight = atoi(p+1); } new_backend->weight = weight; } if (0 != network_address_set_address(new_backend->addr, address)) { network_backend_free(new_backend); return -1; } /* check if this backend is already known */ g_mutex_lock(bs->backends_mutex); /*remove lock*/ gint first_slave = -1; for (i = 0; i < bs->backends->len; i++) { network_backend_t *old_backend = bs->backends->pdata[i]; if (first_slave == -1 && old_backend->type == BACKEND_TYPE_RO) first_slave = i; if (old_backend->type == type && strleq(S(old_backend->addr->name), S(new_backend->addr->name))) { network_backend_free(new_backend); g_mutex_unlock(bs->backends_mutex); /*remove lock*/ g_critical("backend %s is already known!", address); return -1; } } g_ptr_array_add(bs->backends, new_backend); if (first_slave != -1 && type == BACKEND_TYPE_RW) { network_backend_t *temp_backend = bs->backends->pdata[first_slave]; bs->backends->pdata[first_slave] = bs->backends->pdata[bs->backends->len - 1]; bs->backends->pdata[bs->backends->len - 1] = temp_backend; } g_mutex_unlock(bs->backends_mutex); /*remove lock*/ g_message("added %s backend: %s", (type == BACKEND_TYPE_RW) ? "read/write" : "read-only", address); if (p != NULL) *p = '@'; return 0; }
/** * init the plugin with the parsed config */ static int network_mysqld_master_plugin_apply_config(chassis *chas, chassis_plugin_config *config) { network_mysqld_con *con; network_socket *listen_sock; if (!config->address) config->address = g_strdup(":4041"); if (!config->master_username) config->master_username = g_strdup("root"); if (!config->master_password) config->master_password = g_strdup("secret"); /** * create a connection handle for the listen socket */ con = network_mysqld_con_new(); network_mysqld_add_connection(chas, con); con->config = config; config->listen_con = con; listen_sock = network_socket_new(); con->server = listen_sock; /* set the plugin hooks as we want to apply them to the new connections too later */ network_mysqld_server_connection_init(con); /* FIXME: network_socket_set_address() */ if (0 != network_address_set_address(listen_sock->dst, config->address)) { return -1; } /* FIXME: network_socket_bind() */ if (0 != network_socket_bind(listen_sock)) { return -1; } /** * call network_mysqld_con_accept() with this connection when we are done */ event_set(&(listen_sock->event), listen_sock->fd, EV_READ|EV_PERSIST, network_mysqld_con_accept, con); event_base_set(chas->event_base, &(listen_sock->event)); event_add(&(listen_sock->event), NULL); return 0; }
/** * test if we decode the port number correctly */ void t_network_address_resolve() { network_address *addr; g_test_bug("43313"); addr = network_address_new(); network_address_set_address(addr, "127.0.0.1:3306"); /* _set_address() should set the port number */ g_assert_cmpint(ntohs(addr->addr.ipv4.sin_port), ==, 3306); /* reset the name to see that _refresh_name() updates to the right value */ g_string_truncate(addr->name, 0); network_address_refresh_name(addr); g_assert_cmpstr(addr->name->str, ==, "127.0.0.1:3306"); network_address_free(addr); }
/** * test if we convert addr->string correctly for IPv6 */ void t_network_address_resolve_ipv6() { network_address *addr; addr = network_address_new(); if (0 != network_address_set_address(addr, "[::1]")) { /* skip test, if resolving ::1 fails */ network_address_free(addr); return; } /* _set_address() should set the port number */ /* reset the name to see that _refresh_name() updates to the right value */ g_string_truncate(addr->name, 0); network_address_refresh_name(addr); g_assert_cmpstr(addr->name->str, ==, "[::1]:3306"); network_address_free(addr); }
/** * connect a socket * * the con->dst->addr has to be set before * * @param con a socket * @return NETWORK_SOCKET_SUCCESS on connected, NETWORK_SOCKET_ERROR on error * * @see network_address_set_address() */ network_socket_retval_t network_socket_bind(network_socket * con) { /* * HPUX: int setsockopt(int s, int level, int optname, const void *optval, int optlen); * all others: int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); */ #define SETSOCKOPT_OPTVAL_CAST (void *) g_return_val_if_fail(con->fd < 0, NETWORK_SOCKET_ERROR); /* socket is already bound */ g_return_val_if_fail((con->socket_type == SOCK_DGRAM) || (con->socket_type == SOCK_STREAM), NETWORK_SOCKET_ERROR); if (con->socket_type == SOCK_STREAM) { g_return_val_if_fail(con->dst, NETWORK_SOCKET_ERROR); g_return_val_if_fail(con->dst->name->len > 0, NETWORK_SOCKET_ERROR); if (-1 == (con->fd = socket(con->dst->addr.common.sa_family, con->socket_type, 0))) { g_critical("%s: socket(%s) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } if (con->dst->addr.common.sa_family == AF_INET || con->dst->addr.common.sa_family == AF_INET6) { /* TCP_NODELAY is int on unix */ /* SO_REUSEADDR is int on unix */ int val; val = 1; if (0 != setsockopt(con->fd, IPPROTO_TCP, TCP_NODELAY, SETSOCKOPT_OPTVAL_CAST &val, sizeof(val))) { g_critical("%s: setsockopt(%s, IPPROTO_TCP, TCP_NODELAY) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } if (0 != setsockopt(con->fd, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_CAST &val, sizeof(val))) { g_critical("%s: setsockopt(%s, SOL_SOCKET, SO_REUSEADDR) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } } if (con->dst->addr.common.sa_family == AF_INET6) { #ifdef IPV6_V6ONLY /* disable dual-stack IPv4-over-IPv6 sockets * * ... if it is supported: * - Linux * - Mac OS X * - FreeBSD * - Solaris 10 and later * * no supported on: * - Solaris 9 and earlier */ /* IPV6_V6ONLY is int on unix */ int val; val = 0; if (0 != setsockopt(con->fd, IPPROTO_IPV6, IPV6_V6ONLY, SETSOCKOPT_OPTVAL_CAST &val, sizeof(val))) { g_critical("%s: setsockopt(%s, IPPROTO_IPV6, IPV6_V6ONLY) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } #endif } if (-1 == bind(con->fd, &con->dst->addr.common, con->dst->len)) { gchar *address_copy; /* binding failed so the address/socket is already being used * let's check if we can connect to it so we check if is being used * by some app */ if (-1 == connect(con->fd, &con->dst->addr.common, con->dst->len)) { g_debug("%s.%d: connect(%s) failed: %s (%d)", __FILE__, __LINE__, con->dst->name->str, g_strerror(errno), errno); /* we can't connect to the socket so no one is listening on it. We need * to unlink it (delete the name from the file system) to be able to * re-use it. * network_address_free does the unlink, but to re-use it we need * to store the pathname associated with the socket before unlink it and * create a new socket with it. */ address_copy = g_strdup(con->dst->name->str); con->dst->can_unlink_socket = TRUE; network_address_free(con->dst); con->dst = network_address_new(); network_address_set_address(con->dst, address_copy); /* we can now free the address copy */ g_free(address_copy); g_debug("%s: retrying to bind(%s)", G_STRLOC, con->dst->name->str); /* let's bind again with the new socket */ if (-1 == bind(con->fd, &con->dst->addr.common, con->dst->len)) { g_critical("%s: bind(%s) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } } else { g_critical("%s: bind(%s) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } } if (con->dst->addr.common.sa_family == AF_INET && con->dst->addr.ipv4.sin_port == 0) { struct sockaddr_in a; socklen_t a_len = sizeof(a); if (0 != getsockname(con->fd, (struct sockaddr *)&a, &a_len)) { g_critical("%s: getsockname(%s) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } con->dst->addr.ipv4.sin_port = a.sin_port; } else if (con->dst->addr.common.sa_family == AF_INET6 && con->dst->addr.ipv6.sin6_port == 0) { struct sockaddr_in6 a; socklen_t a_len = sizeof(a); if (0 != getsockname(con->fd, (struct sockaddr *)&a, &a_len)) { g_critical("%s: getsockname(%s) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } con->dst->addr.ipv6.sin6_port = a.sin6_port; } if (-1 == listen(con->fd, 128)) { g_critical("%s: listen(%s, 128) failed: %s (%d)", G_STRLOC, con->dst->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } } else { /* UDP sockets bind the ->src address */ g_return_val_if_fail(con->src, NETWORK_SOCKET_ERROR); g_return_val_if_fail(con->src->name->len > 0, NETWORK_SOCKET_ERROR); if (-1 == (con->fd = socket(con->src->addr.common.sa_family, con->socket_type, 0))) { g_critical("%s: socket(%s) failed: %s (%d)", G_STRLOC, con->src->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } if (-1 == bind(con->fd, &con->src->addr.common, con->src->len)) { g_critical("%s: bind(%s) failed: %s (%d)", G_STRLOC, con->src->name->str, g_strerror(errno), errno); return NETWORK_SOCKET_ERROR; } } con->dst->can_unlink_socket = TRUE; return NETWORK_SOCKET_SUCCESS; }