1 /*
2 * Copyright (C) 2012-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 #include <iostream>
20 #include <cstdio>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #ifdef COMPILER_MSVC
26 #undef O_NONBLOCK
27 #define O_NONBLOCK 0
28 #endif
29 #if defined(PLATFORM_WINDOWS)
30 #include <winsock2.h>
31 #include <ws2tcpip.h>
32 #else
33 #include <netdb.h>
34 #endif
35
36 #if defined(PLATFORM_WINDOWS)
37 typedef int socklen_t;
38 #else
39 #include <unistd.h>
40 #include <sys/ioctl.h>
closesocket(int s)41 inline void closesocket(int s) { ::close(s); }
42 #endif
43
44 #include "pbd/xml++.h"
45 #include "pbd/error.h"
46 #include "pbd/failed_constructor.h"
47 #include "pbd/convert.h"
48 #include "pbd/compose.h"
49
50 #include "midi++/types.h"
51 #include "midi++/ipmidi_port.h"
52 #include "midi++/channel.h"
53
54 using namespace MIDI;
55 using namespace std;
56 using namespace PBD;
57
IPMIDIPort(int base_port,const string & iface)58 IPMIDIPort::IPMIDIPort (int base_port, const string& iface)
59 : Port (string_compose ("IPmidi@%1", base_port), Port::Flags (Port::IsInput|Port::IsOutput))
60 , sockin (-1)
61 , sockout (-1)
62 {
63 if (!open_sockets (base_port, iface)) {
64 throw (failed_constructor ());
65 }
66 }
67
IPMIDIPort(const XMLNode & node)68 IPMIDIPort::IPMIDIPort (const XMLNode& node)
69 : Port (node)
70 {
71 /* base class does not class set_state() */
72 set_state (node);
73 }
74
~IPMIDIPort()75 IPMIDIPort::~IPMIDIPort ()
76 {
77 close_sockets ();
78 }
79
80 int
selectable() const81 IPMIDIPort::selectable () const
82 {
83 return sockin;
84 }
85
86 XMLNode&
get_state() const87 IPMIDIPort::get_state () const
88 {
89 return Port::get_state ();
90 }
91
92 void
set_state(const XMLNode & node)93 IPMIDIPort::set_state (const XMLNode& node)
94 {
95 Port::set_state (node);
96 }
97
98 void
close_sockets()99 IPMIDIPort::close_sockets ()
100 {
101 if (sockin >= 0) {
102 ::closesocket (sockin);
103 sockin = -1;
104 }
105
106 if (sockout >= 0) {
107 ::closesocket (sockout);
108 sockout = -1;
109 }
110 }
111
112 #ifndef PLATFORM_WINDOWS
113 static bool
get_address(int sock,struct in_addr * inaddr,const string & ifname)114 get_address (int sock, struct in_addr *inaddr, const string& ifname )
115 {
116 // Get interface address from supplied name.
117
118 struct ifreq ifr;
119 ::strncpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
120
121 if (::ioctl(sock, SIOCGIFFLAGS, (char *) &ifr)) {
122 ::perror("ioctl(SIOCGIFFLAGS)");
123 return false;
124 }
125
126 if ((ifr.ifr_flags & IFF_UP) == 0) {
127 error << string_compose ("interface %1 is down", ifname) << endmsg;
128 return false;
129 }
130
131 if (::ioctl(sock, SIOCGIFADDR, (char *) &ifr)) {
132 ::perror("ioctl(SIOCGIFADDR)");
133 return false;
134 }
135
136 struct sockaddr_in sa;
137 ::memcpy(&sa, &ifr.ifr_addr, sizeof(struct sockaddr_in));
138 inaddr->s_addr = sa.sin_addr.s_addr;
139
140 return true;
141 }
142 #endif
143
144 bool
open_sockets(int base_port,const string & ifname)145 IPMIDIPort::open_sockets (int base_port, const string& ifname)
146 {
147 int protonum = 0;
148 struct protoent *proto = ::getprotobyname("IP");
149
150 if (proto) {
151 protonum = proto->p_proto;
152 }
153
154 sockin = ::socket (PF_INET, SOCK_DGRAM, protonum);
155 if (sockin < 0) {
156 ::perror("socket(in)");
157 return false;
158 }
159
160 struct sockaddr_in addrin;
161 ::memset(&addrin, 0, sizeof(addrin));
162 addrin.sin_family = AF_INET;
163 addrin.sin_addr.s_addr = htonl(INADDR_ANY);
164 addrin.sin_port = htons(base_port);
165
166 if (::bind(sockin, (struct sockaddr *) (&addrin), sizeof(addrin)) < 0) {
167 ::perror("bind");
168 return false;
169 }
170
171 // Will Hall, 2007
172 // INADDR_ANY will bind to default interface,
173 // specify alternate interface nameon which to bind...
174 struct in_addr if_addr_in;
175 #ifndef PLATFORM_WINDOWS
176 if (!ifname.empty()) {
177 if (!get_address(sockin, &if_addr_in, ifname)) {
178 error << string_compose ("socket(in): could not find interface address for %1", ifname) << endmsg;
179 return false;
180 }
181 if (::setsockopt(sockin, IPPROTO_IP, IP_MULTICAST_IF,
182 (char *) &if_addr_in, sizeof(if_addr_in))) {
183 ::perror("setsockopt(IP_MULTICAST_IF)");
184 return false;
185 }
186 } else {
187 if_addr_in.s_addr = htonl (INADDR_ANY);
188 }
189 #else
190 if_addr_in.s_addr = htonl (INADDR_ANY);
191 #endif
192
193 struct ip_mreq mreq;
194 mreq.imr_multiaddr.s_addr = ::inet_addr("225.0.0.37"); /* ipMIDI group multicast address */
195 mreq.imr_interface.s_addr = if_addr_in.s_addr;
196 if(::setsockopt (sockin, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)) < 0) {
197 ::perror("setsockopt(IP_ADD_MEMBERSHIP)");
198 fprintf(stderr, "socket(in): your kernel is probably missing multicast support.\n");
199 return false;
200 }
201
202 // Output socket...
203
204 sockout = ::socket (AF_INET, SOCK_DGRAM, protonum);
205
206 if (sockout < 0) {
207 ::perror("socket(out)");
208 return false;
209 }
210
211 // Will Hall, Oct 2007
212 #ifndef PLATFORM_WINDOWS
213 if (!ifname.empty()) {
214 struct in_addr if_addr_out;
215 if (!get_address(sockout, &if_addr_out, ifname)) {
216 error << string_compose ("socket(out): could not find interface address for %1", ifname) << endmsg;
217 return false;
218 }
219 if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_IF, (char *) &if_addr_out, sizeof(if_addr_out))) {
220 ::perror("setsockopt(IP_MULTICAST_IF)");
221 return false;
222 }
223 }
224 #endif
225
226 ::memset(&addrout, 0, sizeof(struct sockaddr_in));
227 addrout.sin_family = AF_INET;
228 addrout.sin_addr.s_addr = ::inet_addr("225.0.0.37");
229 addrout.sin_port = htons (base_port);
230
231 // Turn off loopback...
232 int loop = 0;
233
234 #ifdef PLATFORM_WINDOWS
235
236 /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms739161%28v=vs.85%29.aspx
237 *
238 * ------------------------------------------------------------------------------
239 * Note The Winsock version of the IP_MULTICAST_LOOP option is
240 * semantically different than the UNIX version of the
241 * IP_MULTICAST_LOOP option:
242 *
243 * In Winsock, the IP_MULTICAST_LOOP option applies only to the receive path.
244 * In the UNIX version, the IP_MULTICAST_LOOP option applies to the send path.
245 *
246 * For example, applications ON and OFF (which are easier to track than
247 * X and Y) join the same group on the same interface; application ON
248 * sets the IP_MULTICAST_LOOP option on, application OFF sets the
249 * IP_MULTICAST_LOOP option off. If ON and OFF are Winsock
250 * applications, OFF can send to ON, but ON cannot sent to OFF. In
251 * contrast, if ON and OFF are UNIX applications, ON can send to OFF,
252 * but OFF cannot send to ON.
253 * ------------------------------------------------------------------------------
254 *
255 * Alles klar? Gut!
256 */
257
258 const int target_sock = sockin;
259 #else
260 const int target_sock = sockout;
261 #endif
262
263 if (::setsockopt (target_sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof (loop)) < 0) {
264 ::perror("setsockopt(IP_MULTICAST_LOOP)");
265 return false;
266 }
267
268 #ifndef PLATFORM_WINDOWS
269
270 if (fcntl (sockin, F_SETFL, O_NONBLOCK)) {
271 error << "cannot set non-blocking mode for IP MIDI input socket (" << ::strerror (errno) << ')' << endmsg;
272 return false;
273 }
274
275 if (fcntl (sockout, F_SETFL, O_NONBLOCK)) {
276 error << "cannot set non-blocking mode for IP MIDI output socket (" << ::strerror (errno) << ')' << endmsg;
277 return false;
278 }
279
280 #else
281 // If imode !=0, non-blocking mode is enabled.
282 u_long mode=1;
283 if (ioctlsocket(sockin,FIONBIO,&mode)) {
284 error << "cannot set non-blocking mode for IP MIDI input socket (" << ::strerror (errno) << ')' << endmsg;
285 return false;
286 }
287 mode = 1; /* just in case it was modified in the previous call */
288 if (ioctlsocket(sockout,FIONBIO,&mode)) {
289 error << "cannot set non-blocking mode for IP MIDI output socket (" << ::strerror (errno) << ')' << endmsg;
290 return false;
291 }
292 #endif
293
294 return true;
295 }
296
297 int
write(const MIDI::byte * msg,size_t msglen,timestamp_t)298 IPMIDIPort::write (const MIDI::byte* msg, size_t msglen, timestamp_t /* ignored */) {
299
300 if (sockout) {
301 Glib::Threads::Mutex::Lock lm (write_lock);
302 if (::sendto (sockout, (const char *) msg, msglen, 0, (struct sockaddr *) &addrout, sizeof(struct sockaddr_in)) < 0) {
303 ::perror("sendto");
304 return -1;
305 }
306 return msglen;
307 }
308 return 0;
309 }
310
311 int
read(MIDI::byte *,size_t)312 IPMIDIPort::read (MIDI::byte* /*buf*/, size_t /*bufsize*/)
313 {
314 /* nothing to do here - all handled by parse() */
315 return 0;
316 }
317
318 void
parse(samplecnt_t timestamp)319 IPMIDIPort::parse (samplecnt_t timestamp)
320 {
321 /* input was detected on the socket, so go get it and hand it to the
322 * parser. This will emit appropriate signals that will be handled
323 * by anyone who cares.
324 */
325
326 unsigned char buf[1024];
327 struct sockaddr_in sender;
328 socklen_t slen = sizeof(sender);
329 int r = ::recvfrom (sockin, (char *) buf, sizeof(buf), 0, (struct sockaddr *) &sender, &slen);
330
331 if (r >= 0) {
332
333 _parser->set_timestamp (timestamp);
334
335 for (int i = 0; i < r; ++i) {
336 _parser->scanner (buf[i]);
337 }
338 } else {
339 ::perror ("failed to recv from socket");
340 }
341 }
342