1 /* 2 * QEMU network listener 3 * 4 * Copyright (c) 2016-2017 Red Hat, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/osdep.h" 22 #include "io/net-listener.h" 23 #include "io/dns-resolver.h" 24 #include "qapi/error.h" 25 26 QIONetListener *qio_net_listener_new(void) 27 { 28 QIONetListener *ret; 29 30 ret = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); 31 32 return ret; 33 } 34 35 void qio_net_listener_set_name(QIONetListener *listener, 36 const char *name) 37 { 38 g_free(listener->name); 39 listener->name = g_strdup(name); 40 } 41 42 43 static gboolean qio_net_listener_channel_func(QIOChannel *ioc, 44 GIOCondition condition, 45 gpointer opaque) 46 { 47 QIONetListener *listener = QIO_NET_LISTENER(opaque); 48 QIOChannelSocket *sioc; 49 50 sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), 51 NULL); 52 if (!sioc) { 53 return TRUE; 54 } 55 56 if (listener->io_func) { 57 listener->io_func(listener, sioc, listener->io_data); 58 } 59 60 object_unref(OBJECT(sioc)); 61 62 return TRUE; 63 } 64 65 66 int qio_net_listener_open_sync(QIONetListener *listener, 67 SocketAddress *addr, 68 Error **errp) 69 { 70 QIODNSResolver *resolver = qio_dns_resolver_get_instance(); 71 SocketAddress **resaddrs; 72 size_t nresaddrs; 73 size_t i; 74 Error *err = NULL; 75 bool success = false; 76 77 if (qio_dns_resolver_lookup_sync(resolver, 78 addr, 79 &nresaddrs, 80 &resaddrs, 81 errp) < 0) { 82 return -1; 83 } 84 85 for (i = 0; i < nresaddrs; i++) { 86 QIOChannelSocket *sioc = qio_channel_socket_new(); 87 88 if (qio_channel_socket_listen_sync(sioc, resaddrs[i], 89 err ? NULL : &err) == 0) { 90 success = true; 91 92 qio_net_listener_add(listener, sioc); 93 } 94 95 qapi_free_SocketAddress(resaddrs[i]); 96 object_unref(OBJECT(sioc)); 97 } 98 g_free(resaddrs); 99 100 if (success) { 101 error_free(err); 102 return 0; 103 } else { 104 error_propagate(errp, err); 105 return -1; 106 } 107 } 108 109 110 void qio_net_listener_add(QIONetListener *listener, 111 QIOChannelSocket *sioc) 112 { 113 if (listener->name) { 114 char *name = g_strdup_printf("%s-listen", listener->name); 115 qio_channel_set_name(QIO_CHANNEL(sioc), name); 116 g_free(name); 117 } 118 119 listener->sioc = g_renew(QIOChannelSocket *, listener->sioc, 120 listener->nsioc + 1); 121 listener->io_source = g_renew(typeof(listener->io_source[0]), 122 listener->io_source, 123 listener->nsioc + 1); 124 listener->sioc[listener->nsioc] = sioc; 125 listener->io_source[listener->nsioc] = NULL; 126 127 object_ref(OBJECT(sioc)); 128 listener->connected = true; 129 130 if (listener->io_func != NULL) { 131 object_ref(OBJECT(listener)); 132 listener->io_source[listener->nsioc] = qio_channel_add_watch_source( 133 QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN, 134 qio_net_listener_channel_func, 135 listener, (GDestroyNotify)object_unref, NULL); 136 } 137 138 listener->nsioc++; 139 } 140 141 142 void qio_net_listener_set_client_func_full(QIONetListener *listener, 143 QIONetListenerClientFunc func, 144 gpointer data, 145 GDestroyNotify notify, 146 GMainContext *context) 147 { 148 size_t i; 149 150 if (listener->io_notify) { 151 listener->io_notify(listener->io_data); 152 } 153 listener->io_func = func; 154 listener->io_data = data; 155 listener->io_notify = notify; 156 157 for (i = 0; i < listener->nsioc; i++) { 158 if (listener->io_source[i]) { 159 g_source_destroy(listener->io_source[i]); 160 g_source_unref(listener->io_source[i]); 161 listener->io_source[i] = NULL; 162 } 163 } 164 165 if (listener->io_func != NULL) { 166 for (i = 0; i < listener->nsioc; i++) { 167 object_ref(OBJECT(listener)); 168 listener->io_source[i] = qio_channel_add_watch_source( 169 QIO_CHANNEL(listener->sioc[i]), G_IO_IN, 170 qio_net_listener_channel_func, 171 listener, (GDestroyNotify)object_unref, context); 172 } 173 } 174 } 175 176 void qio_net_listener_set_client_func(QIONetListener *listener, 177 QIONetListenerClientFunc func, 178 gpointer data, 179 GDestroyNotify notify) 180 { 181 qio_net_listener_set_client_func_full(listener, func, data, 182 notify, NULL); 183 } 184 185 struct QIONetListenerClientWaitData { 186 QIOChannelSocket *sioc; 187 GMainLoop *loop; 188 }; 189 190 191 static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc, 192 GIOCondition condition, 193 gpointer opaque) 194 { 195 struct QIONetListenerClientWaitData *data = opaque; 196 QIOChannelSocket *sioc; 197 198 sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), 199 NULL); 200 if (!sioc) { 201 return TRUE; 202 } 203 204 if (data->sioc) { 205 object_unref(OBJECT(sioc)); 206 } else { 207 data->sioc = sioc; 208 g_main_loop_quit(data->loop); 209 } 210 211 return TRUE; 212 } 213 214 QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener) 215 { 216 GMainContext *ctxt = g_main_context_new(); 217 GMainLoop *loop = g_main_loop_new(ctxt, TRUE); 218 GSource **sources; 219 struct QIONetListenerClientWaitData data = { 220 .sioc = NULL, 221 .loop = loop 222 }; 223 size_t i; 224 225 for (i = 0; i < listener->nsioc; i++) { 226 if (listener->io_source[i]) { 227 g_source_destroy(listener->io_source[i]); 228 g_source_unref(listener->io_source[i]); 229 listener->io_source[i] = NULL; 230 } 231 } 232 233 sources = g_new0(GSource *, listener->nsioc); 234 for (i = 0; i < listener->nsioc; i++) { 235 sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]), 236 G_IO_IN); 237 238 g_source_set_callback(sources[i], 239 (GSourceFunc)qio_net_listener_wait_client_func, 240 &data, 241 NULL); 242 g_source_attach(sources[i], ctxt); 243 } 244 245 g_main_loop_run(loop); 246 247 for (i = 0; i < listener->nsioc; i++) { 248 g_source_unref(sources[i]); 249 } 250 g_free(sources); 251 g_main_loop_unref(loop); 252 g_main_context_unref(ctxt); 253 254 if (listener->io_func != NULL) { 255 for (i = 0; i < listener->nsioc; i++) { 256 object_ref(OBJECT(listener)); 257 listener->io_source[i] = qio_channel_add_watch_source( 258 QIO_CHANNEL(listener->sioc[i]), G_IO_IN, 259 qio_net_listener_channel_func, 260 listener, (GDestroyNotify)object_unref, NULL); 261 } 262 } 263 264 return data.sioc; 265 } 266 267 void qio_net_listener_disconnect(QIONetListener *listener) 268 { 269 size_t i; 270 271 if (!listener->connected) { 272 return; 273 } 274 275 for (i = 0; i < listener->nsioc; i++) { 276 if (listener->io_source[i]) { 277 g_source_destroy(listener->io_source[i]); 278 g_source_unref(listener->io_source[i]); 279 listener->io_source[i] = NULL; 280 } 281 qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL); 282 } 283 listener->connected = false; 284 } 285 286 287 bool qio_net_listener_is_connected(QIONetListener *listener) 288 { 289 return listener->connected; 290 } 291 292 static void qio_net_listener_finalize(Object *obj) 293 { 294 QIONetListener *listener = QIO_NET_LISTENER(obj); 295 size_t i; 296 297 qio_net_listener_disconnect(listener); 298 299 for (i = 0; i < listener->nsioc; i++) { 300 object_unref(OBJECT(listener->sioc[i])); 301 } 302 g_free(listener->io_source); 303 g_free(listener->sioc); 304 g_free(listener->name); 305 } 306 307 static const TypeInfo qio_net_listener_info = { 308 .parent = TYPE_OBJECT, 309 .name = TYPE_QIO_NET_LISTENER, 310 .instance_size = sizeof(QIONetListener), 311 .instance_finalize = qio_net_listener_finalize, 312 .class_size = sizeof(QIONetListenerClass), 313 }; 314 315 316 static void qio_net_listener_register_types(void) 317 { 318 type_register_static(&qio_net_listener_info); 319 } 320 321 322 type_init(qio_net_listener_register_types); 323