Chris@4: /* Chris@4: * Copyright (C) 2004 Steve Harris Chris@4: * Chris@4: * This program is free software; you can redistribute it and/or modify Chris@4: * it under the terms of the GNU Lesser General Public License as Chris@4: * published by the Free Software Foundation; either version 2.1 of the Chris@4: * License, or (at your option) any later version. Chris@4: * Chris@4: * This program is distributed in the hope that it will be useful, Chris@4: * but WITHOUT ANY WARRANTY; without even the implied warranty of Chris@4: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Chris@4: * GNU Lesser General Public License for more details. Chris@4: * Chris@4: * $Id$ Chris@4: */ Chris@4: Chris@4: #ifdef HAVE_CONFIG_H Chris@4: #include "config.h" Chris@4: #endif Chris@4: Chris@4: #include Chris@4: #include Chris@4: #include Chris@4: #include Chris@4: #include Chris@4: #include Chris@4: Chris@4: #ifdef _MSC_VER Chris@4: #include Chris@4: #else Chris@4: #include Chris@4: #endif Chris@4: Chris@4: #ifdef WIN32 Chris@4: #include Chris@4: #include Chris@4: #else Chris@4: #include Chris@4: #include Chris@4: #include Chris@4: #endif Chris@4: Chris@4: #include "lo_types_internal.h" Chris@4: #include "lo/lo.h" Chris@4: Chris@4: #ifndef MSG_NOSIGNAL Chris@4: #define MSG_NOSIGNAL 0 Chris@4: #endif Chris@4: Chris@4: #ifdef WIN32 Chris@4: int initWSock(); Chris@4: #endif Chris@4: Chris@4: #ifdef WIN32 Chris@4: #define geterror() WSAGetLastError() Chris@4: #else Chris@4: #define geterror() errno Chris@4: #endif Chris@4: Chris@4: static int resolve_address(lo_address a); Chris@4: static int create_socket(lo_address a); Chris@4: static int send_data(lo_address a, lo_server from, char *data, const size_t data_len); Chris@4: Chris@4: // message.c Chris@4: int lo_message_add_varargs_internal(lo_message m, const char *types, va_list ap, Chris@4: const char *file, int line); Chris@4: Chris@4: Chris@4: Chris@4: /* Don't call lo_send_internal directly, use lo_send, a macro wrapping this Chris@4: * function with appropriate values for file and line */ Chris@4: Chris@4: #ifdef __GNUC__ Chris@4: int lo_send_internal(lo_address t, const char *file, const int line, Chris@4: const char *path, const char *types, ...) Chris@4: #else Chris@4: int lo_send(lo_address t, const char *path, const char *types, ...) Chris@4: #endif Chris@4: { Chris@4: va_list ap; Chris@4: int ret; Chris@4: #ifndef __GNUC__ Chris@4: const char *file = ""; Chris@4: int line = 0; Chris@4: #endif Chris@4: Chris@4: lo_message msg = lo_message_new(); Chris@4: Chris@4: t->errnum = 0; Chris@4: t->errstr = NULL; Chris@4: Chris@4: va_start(ap, types); Chris@4: ret = lo_message_add_varargs_internal(msg, types, ap, file, line); Chris@4: Chris@4: if (ret) { Chris@4: lo_message_free(msg); Chris@4: t->errnum = ret; Chris@4: if (ret == -1) t->errstr = "unknown type"; Chris@4: else t->errstr = "bad format/args"; Chris@4: return ret; Chris@4: } Chris@4: Chris@4: ret = lo_send_message(t, path, msg); Chris@4: lo_message_free(msg); Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: Chris@4: /* Don't call lo_send_timestamped_internal directly, use lo_send_timestamped, a Chris@4: * macro wrapping this function with appropriate values for file and line */ Chris@4: Chris@4: #ifdef __GNUC__ Chris@4: int lo_send_timestamped_internal(lo_address t, const char *file, Chris@4: const int line, lo_timetag ts, Chris@4: const char *path, const char *types, ...) Chris@4: #else Chris@4: int lo_send_timestamped(lo_address t, lo_timetag ts, Chris@4: const char *path, const char *types, ...) Chris@4: #endif Chris@4: { Chris@4: va_list ap; Chris@4: int ret; Chris@4: Chris@4: lo_message msg = lo_message_new(); Chris@4: lo_bundle b = lo_bundle_new(ts); Chris@4: Chris@4: #ifndef __GNUC__ Chris@4: const char *file = ""; Chris@4: int line = 0; Chris@4: #endif Chris@4: Chris@4: t->errnum = 0; Chris@4: t->errstr = NULL; Chris@4: Chris@4: va_start(ap, types); Chris@4: ret = lo_message_add_varargs_internal(msg, types, ap, file, line); Chris@4: Chris@4: if (t->errnum) { Chris@4: lo_message_free(msg); Chris@4: return t->errnum; Chris@4: } Chris@4: Chris@4: lo_bundle_add_message(b, path, msg); Chris@4: ret = lo_send_bundle(t, b); Chris@4: lo_message_free(msg); Chris@4: lo_bundle_free(b); Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: /* Don't call lo_send_from_internal directly, use macros wrapping this Chris@4: * function with appropriate values for file and line */ Chris@4: Chris@4: #ifdef __GNUC__ Chris@4: int lo_send_from_internal(lo_address to, lo_server from, const char *file, Chris@4: const int line, lo_timetag ts, Chris@4: const char *path, const char *types, ...) Chris@4: #else Chris@4: int lo_send_from(lo_address to, lo_server from, lo_timetag ts, Chris@4: const char *path, const char *types, ...) Chris@4: #endif Chris@4: { Chris@4: lo_bundle b = NULL; Chris@4: va_list ap; Chris@4: int ret; Chris@4: Chris@4: #ifndef __GNUC__ Chris@4: const char *file = ""; Chris@4: int line = 0; Chris@4: #endif Chris@4: Chris@4: lo_message msg = lo_message_new(); Chris@4: if (ts.sec!=LO_TT_IMMEDIATE.sec || ts.frac!=LO_TT_IMMEDIATE.frac) Chris@4: b = lo_bundle_new(ts); Chris@4: Chris@4: // Clear any previous errors Chris@4: to->errnum = 0; Chris@4: to->errstr = NULL; Chris@4: Chris@4: va_start(ap, types); Chris@4: ret = lo_message_add_varargs_internal(msg, types, ap, file, line); Chris@4: Chris@4: if (to->errnum) { Chris@4: if (b) lo_bundle_free(b); Chris@4: lo_message_free(msg); Chris@4: return to->errnum; Chris@4: } Chris@4: Chris@4: if (b) { Chris@4: lo_bundle_add_message(b, path, msg); Chris@4: ret = lo_send_bundle_from(to, from, b); Chris@4: } else { Chris@4: ret = lo_send_message_from(to, from, path, msg); Chris@4: } Chris@4: Chris@4: // Free-up memory Chris@4: lo_message_free(msg); Chris@4: if (b) lo_bundle_free(b); Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: Chris@4: #if 0 Chris@4: Chris@4: This (incomplete) function converts from printf-style formats to OSC typetags, Chris@4: but I think its dangerous and mislieading so its not available at the moment. Chris@4: Chris@4: static char *format_to_types(const char *format); Chris@4: Chris@4: static char *format_to_types(const char *format) Chris@4: { Chris@4: const char *ptr; Chris@4: char *types = malloc(sizeof(format) + 1); Chris@4: char *out = types; Chris@4: int inspec = 0; Chris@4: int width = 0; Chris@4: int number = 0; Chris@4: Chris@4: if (!format) { Chris@4: return NULL; Chris@4: } Chris@4: Chris@4: for (ptr = format; *ptr; ptr++) { Chris@4: if (inspec) { Chris@4: if (*ptr == 'l') { Chris@4: width++; Chris@4: } else if (*ptr >= '0' && *ptr <= '9') { Chris@4: number *= 10; Chris@4: number += *ptr - '0'; Chris@4: } else if (*ptr == 'd') { Chris@4: if (width < 2 && number < 64) { Chris@4: *out++ = LO_INT32; Chris@4: } else { Chris@4: *out++ = LO_INT64; Chris@4: } Chris@4: } else if (*ptr == 'f') { Chris@4: if (width < 2 && number < 64) { Chris@4: *out++ = LO_FLOAT; Chris@4: } else { Chris@4: *out++ = LO_DOUBLE; Chris@4: } Chris@4: } else if (*ptr == '%') { Chris@4: fprintf(stderr, "liblo warning, unexpected '%%' in format\n"); Chris@4: inspec = 1; Chris@4: width = 0; Chris@4: number = 0; Chris@4: } else { Chris@4: fprintf(stderr, "liblo warning, unrecognised character '%c' " Chris@4: "in format\n", *ptr); Chris@4: } Chris@4: } else { Chris@4: if (*ptr == '%') { Chris@4: inspec = 1; Chris@4: width = 0; Chris@4: number = 0; Chris@4: } else if (*ptr == LO_TRUE || *ptr == LO_FALSE || *ptr == LO_NIL || Chris@4: *ptr == LO_INFINITUM) { Chris@4: *out++ = *ptr; Chris@4: } else { Chris@4: fprintf(stderr, "liblo warning, unrecognised character '%c' " Chris@4: "in format\n", *ptr); Chris@4: } Chris@4: } Chris@4: } Chris@4: *out++ = '\0'; Chris@4: Chris@4: return types; Chris@4: } Chris@4: Chris@4: #endif Chris@4: Chris@4: Chris@4: static int resolve_address(lo_address a) Chris@4: { Chris@4: int ret; Chris@4: Chris@4: if (a->protocol == LO_UDP || a->protocol == LO_TCP) { Chris@4: struct addrinfo *ai; Chris@4: struct addrinfo hints; Chris@4: Chris@4: memset(&hints, 0, sizeof(hints)); Chris@4: #ifdef ENABLE_IPV6 Chris@4: hints.ai_family = PF_UNSPEC; Chris@4: #else Chris@4: hints.ai_family = PF_INET; Chris@4: #endif Chris@4: hints.ai_socktype = a->protocol == LO_UDP ? SOCK_DGRAM : SOCK_STREAM; Chris@4: Chris@4: if ((ret = getaddrinfo(a->host, a->port, &hints, &ai))) { Chris@4: a->errnum = ret; Chris@4: a->errstr = gai_strerror(ret); Chris@4: a->ai = NULL; Chris@4: return -1; Chris@4: } Chris@4: Chris@4: a->ai = ai; Chris@4: } Chris@4: Chris@4: return 0; Chris@4: } Chris@4: Chris@4: static int create_socket(lo_address a) Chris@4: { Chris@4: if (a->protocol == LO_UDP || a->protocol == LO_TCP) { Chris@4: Chris@4: a->socket = socket(a->ai->ai_family, a->ai->ai_socktype, 0); Chris@4: if (a->socket == -1) { Chris@4: a->errnum = geterror(); Chris@4: a->errstr = NULL; Chris@4: return -1; Chris@4: } Chris@4: Chris@4: if (a->protocol == LO_TCP) { Chris@4: // Only call connect() for TCP sockets - we use sendto() for UDP Chris@4: if ((connect(a->socket, a->ai->ai_addr, a->ai->ai_addrlen))) { Chris@4: a->errnum = geterror(); Chris@4: a->errstr = NULL; Chris@4: close(a->socket); Chris@4: a->socket = -1; Chris@4: return -1; Chris@4: } Chris@4: } Chris@4: // if UDP and destination address is broadcast allow broadcast on the Chris@4: // socket Chris@4: else if (a->protocol == LO_UDP && a->ai->ai_family == AF_INET) Chris@4: { Chris@4: // If UDP, and destination address is broadcast, Chris@4: // then allow broadcast on the socket. Chris@4: struct sockaddr_in* si = (struct sockaddr_in*)a->ai->ai_addr; Chris@4: unsigned char* ip = (unsigned char*)&(si->sin_addr); Chris@4: Chris@4: if (ip[0]==255 && ip[1]==255 && ip[2]==255 && ip[3]==255) Chris@4: { Chris@4: int opt = 1; Chris@4: setsockopt(a->socket, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int)); Chris@4: } Chris@4: } Chris@4: Chris@4: } Chris@4: #ifndef WIN32 Chris@4: else if (a->protocol == LO_UNIX) { Chris@4: struct sockaddr_un sa; Chris@4: Chris@4: a->socket = socket(PF_UNIX, SOCK_DGRAM, 0); Chris@4: if (a->socket == -1) { Chris@4: a->errnum = geterror(); Chris@4: a->errstr = NULL; Chris@4: return -1; Chris@4: } Chris@4: Chris@4: sa.sun_family = AF_UNIX; Chris@4: strncpy(sa.sun_path, a->port, sizeof(sa.sun_path)-1); Chris@4: Chris@4: if ((connect(a->socket, (struct sockaddr *)&sa, sizeof(sa))) < 0) { Chris@4: a->errnum = geterror(); Chris@4: a->errstr = NULL; Chris@4: close(a->socket); Chris@4: a->socket = -1; Chris@4: return -1; Chris@4: } Chris@4: } Chris@4: #endif Chris@4: else { Chris@4: /* unknown protocol */ Chris@4: return -2; Chris@4: } Chris@4: Chris@4: return 0; Chris@4: } Chris@4: Chris@4: static int send_data(lo_address a, lo_server from, char *data, const size_t data_len) Chris@4: { Chris@4: int ret=0; Chris@4: int sock=-1; Chris@4: Chris@4: #ifdef WIN32 Chris@4: if(!initWSock()) return -1; Chris@4: #endif Chris@4: Chris@4: if (data_len > LO_MAX_MSG_SIZE) { Chris@4: a->errnum = 99; Chris@4: a->errstr = "Attempted to send message in excess of maximum " Chris@4: "message size"; Chris@4: return -1; Chris@4: } Chris@4: Chris@4: // Resolve the destination address, if not done already Chris@4: if (!a->ai) { Chris@4: ret = resolve_address( a ); Chris@4: if (ret) return ret; Chris@4: } Chris@4: Chris@4: // Re-use existing socket? Chris@4: if (from) { Chris@4: sock = from->sockets[0].fd; Chris@4: } else if (a->protocol == LO_UDP && lo_client_sockets.udp!=-1) { Chris@4: sock = lo_client_sockets.udp; Chris@4: } else { Chris@4: if (a->socket==-1) { Chris@4: ret = create_socket( a ); Chris@4: if (ret) return ret; Chris@4: } Chris@4: sock = a->socket; Chris@4: } Chris@4: Chris@4: Chris@4: Chris@4: // Send Length of the following data Chris@4: if (a->protocol == LO_TCP) { Chris@4: int32_t size = htonl(data_len); Chris@4: ret = send(sock, &size, sizeof(size), MSG_NOSIGNAL); Chris@4: } Chris@4: Chris@4: // Send the data Chris@4: if (a->protocol == LO_UDP) { Chris@4: if (a->ttl >= 0) { Chris@4: unsigned char ttl = (unsigned char)a->ttl; Chris@4: setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl)); Chris@4: } Chris@4: ret = sendto(sock, data, data_len, MSG_NOSIGNAL, Chris@4: a->ai->ai_addr, a->ai->ai_addrlen); Chris@4: } else { Chris@4: ret = send(sock, data, data_len, MSG_NOSIGNAL); Chris@4: } Chris@4: Chris@4: if (a->protocol == LO_TCP && ret == -1) { Chris@4: close(a->socket); Chris@4: a->socket=-1; Chris@4: } Chris@4: Chris@4: if (ret == -1) { Chris@4: a->errnum = geterror(); Chris@4: a->errstr = NULL; Chris@4: } else { Chris@4: a->errnum = 0; Chris@4: a->errstr = NULL; Chris@4: } Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: Chris@4: int lo_send_message(lo_address a, const char *path, lo_message msg) Chris@4: { Chris@4: return lo_send_message_from( a, NULL, path, msg ); Chris@4: } Chris@4: Chris@4: int lo_send_message_from(lo_address a, lo_server from, const char *path, lo_message msg) Chris@4: { Chris@4: const size_t data_len = lo_message_length(msg, path); Chris@4: char *data = lo_message_serialise(msg, path, NULL, NULL); Chris@4: Chris@4: // Send the message Chris@4: int ret = send_data( a, from, data, data_len ); Chris@4: Chris@4: // For TCP, retry once if it failed. The first try will return Chris@4: // error if the connection was closed, so the second try will Chris@4: // attempt to re-open the connection. Chris@4: if (ret == -1 && a->protocol == LO_TCP) Chris@4: ret = send_data( a, from, data, data_len ); Chris@4: Chris@4: // Free the memory allocated by lo_message_serialise Chris@4: if (data) free( data ); Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: Chris@4: int lo_send_bundle(lo_address a, lo_bundle b) Chris@4: { Chris@4: return lo_send_bundle_from( a, NULL, b ); Chris@4: } Chris@4: Chris@4: Chris@4: int lo_send_bundle_from(lo_address a, lo_server from, lo_bundle b) Chris@4: { Chris@4: const size_t data_len = lo_bundle_length(b); Chris@4: char *data = lo_bundle_serialise(b, NULL, NULL); Chris@4: Chris@4: // Send the bundle Chris@4: int ret = send_data( a, from, data, data_len ); Chris@4: Chris@4: // Free the memory allocated by lo_bundle_serialise Chris@4: if (data) free( data ); Chris@4: Chris@4: return ret; Chris@4: } Chris@4: Chris@4: /* vi:set ts=8 sts=4 sw=4: */