From b34a3f142495da9a2879b6ff13623d9a50dcfb89 Mon Sep 17 00:00:00 2001
From: cbdev <cb@cbcdn.com>
Date: Fri, 17 Apr 2020 23:22:16 +0200
Subject: Extend Windows error reporting, refactor apple command transmission

---
 backends/rtpmidi.c | 105 +++++++++++++++++++++++++++--------------------------
 backends/rtpmidi.h |   7 ++--
 2 files changed, 58 insertions(+), 54 deletions(-)

diff --git a/backends/rtpmidi.c b/backends/rtpmidi.c
index b7d938b..85665fd 100644
--- a/backends/rtpmidi.c
+++ b/backends/rtpmidi.c
@@ -238,7 +238,7 @@ bail:
 
 //TODO this should be trimmed down a bit
 static int rtpmidi_announce_addrs(){
-	char repr[INET6_ADDRSTRLEN + 1] = "", iface[1024] = "";
+	char repr[INET6_ADDRSTRLEN + 1] = "", iface[2048] = "";
 	union {
 		struct sockaddr_in* in4;
 		struct sockaddr_in6* in6;
@@ -247,13 +247,16 @@ static int rtpmidi_announce_addrs(){
 
 	#ifdef _WIN32
 	IP_ADAPTER_UNICAST_ADDRESS_LH* unicast_addr = NULL;
-	IP_ADAPTER_ADDRESSES addrs[50] , *iter = NULL;
+	IP_ADAPTER_ADDRESSES addrs[250] , *iter = NULL;
 	size_t bytes_alloc = sizeof(addrs);
 
-	if(GetAdaptersAddresses(0, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
-				NULL, addrs, (unsigned long*) &bytes_alloc) != ERROR_SUCCESS){
+	unsigned long status = GetAdaptersAddresses(0, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
+			NULL, addrs, (unsigned long*) &bytes_alloc);
+	if(status != ERROR_SUCCESS){
 		//FIXME might try to resize the result list and retry at some point...
-		LOG("Failed to query local interface addresses");
+		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, status,
+				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), iface, sizeof(iface), NULL);
+		LOGPF("Failed to query local interface addresses (%lu): %s", status, iface);
 		return 1;
 	}
 
@@ -401,11 +404,12 @@ static char* rtpmidi_type_name(uint8_t type){
 	return "unknown";
 }
 
-static int rtpmidi_push_peer(rtpmidi_instance_data* data, struct sockaddr_storage sock_addr, socklen_t sock_len, uint8_t learned, uint8_t connected){
+static int rtpmidi_push_peer(rtpmidi_instance_data* data, struct sockaddr_storage sock_addr, socklen_t sock_len, uint8_t learned, uint8_t connected, ssize_t invite_reference){
 	size_t u, p = data->peers;
 
 	for(u = 0; u < data->peers; u++){
 		//check whether the peer is already in the list
+		//TODO this probably should take into account the invite_reference (-1 for initiator peers or if unknown but may be present)
 		if(data->peer[u].active
 				&& sock_len == data->peer[u].dest_len
 				&& !memcmp(&data->peer[u].dest, &sock_addr, sock_len)){
@@ -433,6 +437,7 @@ static int rtpmidi_push_peer(rtpmidi_instance_data* data, struct sockaddr_storag
 	data->peer[p].active = 1;
 	data->peer[p].learned = learned;
 	data->peer[p].connected = connected;
+	data->peer[p].invite = invite_reference;
 	data->peer[p].dest = sock_addr;
 	data->peer[p].dest_len = sock_len;
 	return 0;
@@ -490,6 +495,37 @@ static int rtpmidi_push_invite(instance* inst, char* peer){
 	return 0;
 }
 
+static ssize_t rtpmidi_applecommand(instance* inst, struct sockaddr* dest, socklen_t dest_len, uint8_t control, applemidi_command command){
+	rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
+	uint8_t frame[RTPMIDI_PACKET_BUFFER] = "";
+
+	apple_command* cmd = (apple_command*) &frame;
+	cmd->res1 = 0xFFFF;
+	cmd->command = htobe16(command);
+	cmd->version = htobe32(2);
+	cmd->token = ((uint32_t) rand()) << 16 | rand();
+	cmd->ssrc = htobe32(data->ssrc);
+
+	//append session name to packet
+	memcpy(frame + sizeof(apple_command), data->title ? data->title : RTPMIDI_DEFAULT_NAME, strlen((data->title ? data->title : RTPMIDI_DEFAULT_NAME)) + 1);
+
+	//FIXME should we match sending/receiving ports? if the reference does this, it should be documented
+	return sendto(control ? data->control_fd : data->fd, frame, sizeof(apple_command) + strlen((data->title ? data->title : RTPMIDI_DEFAULT_NAME)) + 1, 0, dest, dest_len);
+}
+
+static ssize_t rtpmidi_peer_applecommand(instance* inst, size_t peer, uint8_t control, applemidi_command command){
+	rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
+	struct sockaddr_storage dest_addr;
+
+	memcpy(&dest_addr, &(data->peer[peer].dest), min(sizeof(dest_addr), data->peer[peer].dest_len));
+	if(control){
+	//calculate remote control port from data port
+		((struct sockaddr_in*) &dest_addr)->sin_port = be16toh(htobe16(((struct sockaddr_in*) &dest_addr)->sin_port) - 1);
+	}
+
+	return rtpmidi_applecommand(inst, (struct sockaddr*) &dest_addr, data->peer[peer].dest_len, control, command);
+}
+
 static int rtpmidi_configure_instance(instance* inst, char* option, char* value){
 	rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
 	char* host = NULL, *port = NULL;
@@ -563,7 +599,7 @@ static int rtpmidi_configure_instance(instance* inst, char* option, char* value)
 			((struct sockaddr_in*) &sock_addr)->sin_port = be16toh(htobe16(((struct sockaddr_in*) &sock_addr)->sin_port) + 1);
 		}
 
-		return rtpmidi_push_peer(data, sock_addr, sock_len, 0, 0);
+		return rtpmidi_push_peer(data, sock_addr, sock_len, 0, 0, -1);
 	}
 	else if(!strcmp(option, "title")){
 		if(data->mode != apple){
@@ -773,56 +809,36 @@ static int rtpmidi_handle_applemidi(instance* inst, int fd, uint8_t* frame, size
 			//accept the invitation
 			LOGPF("Instance %s accepting invitation to session %s%s", inst->name, session_name ? session_name : "UNNAMED", (fd == data->control_fd) ? " (control)":"");
 			//send accept message
-			apple_command* accept = (apple_command*) response;
-			accept->res1 = 0xFFFF;
-			accept->command = htobe16(apple_accept);
-			accept->version = htobe32(2);
-			accept->token = command->token;
-			accept->ssrc = htobe32(data->ssrc);
-			//add local name to response
-			//FIXME use instance title instead of mdns_name
-			memcpy(response + sizeof(apple_command), cfg.mdns_name ? cfg.mdns_name : RTPMIDI_DEFAULT_NAME, strlen((cfg.mdns_name ? cfg.mdns_name : RTPMIDI_DEFAULT_NAME)) + 1);
-			sendto(fd, response, sizeof(apple_command) + strlen(cfg.mdns_name ? cfg.mdns_name : RTPMIDI_DEFAULT_NAME) + 1, 0, (struct sockaddr*) peer, peer_len);
+			//TODO accept->token = command->token;
+			rtpmidi_applecommand(inst, (struct sockaddr*) peer, peer_len, (fd == data->control_fd) ? 1 : 0, apple_accept);
 
 			//push peer
 			if(fd != data->control_fd){
-				return rtpmidi_push_peer(data, *peer, peer_len, 1, 1);
+				return rtpmidi_push_peer(data, *peer, peer_len, 1, 1, -1);
 			}
 			return 0;
 		}
 		else{
 			//send reject message
 			LOGPF("Instance %s rejecting invitation to session %s", inst->name, session_name ? session_name : "UNNAMED");
-			apple_command reject = {
-				.res1 = 0xFFFF,
-				.command = htobe16(apple_reject),
-				.version = htobe32(2),
-				.token = command->token,
-				.ssrc = htobe32(data->ssrc)
-			};
-			sendto(fd, (uint8_t*) &reject, sizeof(apple_command), 0, (struct sockaddr*) peer, peer_len);
+			//TODO .token = command->token,
+			rtpmidi_applecommand(inst, (struct sockaddr*) peer, peer_len, (fd == data->control_fd) ? 1 : 0, apple_reject);
 		}
 		return 0;
 	}
 	else if(command->command == apple_accept){
 		if(fd != data->control_fd){
 			LOGPF("Instance %s negotiated new peer", inst->name);
-			return rtpmidi_push_peer(data, *peer, peer_len, 1, 1);
+			return rtpmidi_push_peer(data, *peer, peer_len, 1, 1, -1);
 			//FIXME store ssrc, start timesync
 		}
 		else{
-			//send invite on data fd
+			//invite peer data port
 			LOGPF("Instance %s peer accepted on control port, inviting data port", inst->name);
-			apple_command* invite = (apple_command*) response;
-			invite->res1 = 0xFFFF;
-			invite->command = htobe16(apple_invite);
-			invite->version = htobe32(2);
-			invite->token = command->token;
-			invite->ssrc = htobe32(data->ssrc);
-			memcpy(response + sizeof(apple_command), data->title ? data->title : RTPMIDI_DEFAULT_NAME, strlen((data->title ? data->title : RTPMIDI_DEFAULT_NAME)) + 1);
 			//calculate data port
 			((struct sockaddr_in*) peer)->sin_port = be16toh(htobe16(((struct sockaddr_in*) peer)->sin_port) + 1);
-			sendto(data->fd, response, sizeof(apple_command) + strlen(data->title ? data->title : RTPMIDI_DEFAULT_NAME) + 1, 0, (struct sockaddr*) peer, peer_len);
+			//send invite
+			rtpmidi_applecommand(inst, (struct sockaddr*) peer, peer_len, 0, apple_invite);
 		}
 		return 0;
 	}
@@ -1064,7 +1080,7 @@ static int rtpmidi_handle_data(instance* inst){
 
 		if(u == data->peers){
 			LOGPF("Learned new peer on %s", inst->name);
-			return rtpmidi_push_peer(data, sock_addr, sock_len, 1, 1);
+			return rtpmidi_push_peer(data, sock_addr, sock_len, 1, 1, -1);
 		}
 	}
 	return 0;
@@ -1281,7 +1297,6 @@ static int rtpmidi_service(){
 	size_t n, u, p;
 	instance** inst = NULL;
 	rtpmidi_instance_data* data = NULL;
-	uint8_t frame[RTPMIDI_PACKET_BUFFER] = "";
 	struct sockaddr_storage control_peer;
 
 	//prepare commands
@@ -1294,11 +1309,6 @@ static int rtpmidi_service(){
 			mm_timestamp() * 10
 		}
 	};
-	apple_command* invite = (apple_command*) &frame;
-	invite->res1 = 0xFFFF;
-	invite->command = htobe16(apple_invite);
-	invite->version = htobe32(2);
-	invite->token = ((uint32_t) rand()) << 16 | rand();
 
 	if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
 		LOG("Failed to fetch instances");
@@ -1329,14 +1339,7 @@ static int rtpmidi_service(){
 				else if(data->peer[p].active && !data->peer[p].learned && (mm_timestamp() / 1000) % 10 == 0){
 					//try to invite pre-defined unconnected applemidi peers
 					DBGPF("Instance %s inviting configured peer %" PRIsize_t, inst[u]->name, p);
-					invite->ssrc = htobe32(data->ssrc);
-					//calculate remote control port from data port
-					memcpy(&control_peer, &(data->peer[u].dest), sizeof(control_peer));
-					((struct sockaddr_in*) &control_peer)->sin_port = be16toh(htobe16(((struct sockaddr_in*) &control_peer)->sin_port) - 1);
-					//append session name to packet
-					memcpy(frame + sizeof(apple_command), data->title ? data->title : RTPMIDI_DEFAULT_NAME, strlen((data->title ? data->title : RTPMIDI_DEFAULT_NAME)) + 1);
-
-					sendto(data->control_fd, (char*) invite, sizeof(apple_command) + strlen((data->title ? data->title : RTPMIDI_DEFAULT_NAME)) + 1, 0, (struct sockaddr*) &control_peer, data->peer[u].dest_len);
+					rtpmidi_peer_applecommand(inst[u], p, 1, apple_invite);
 				}
 			}
 		}
diff --git a/backends/rtpmidi.h b/backends/rtpmidi.h
index de610cd..a5d4f4c 100644
--- a/backends/rtpmidi.h
+++ b/backends/rtpmidi.h
@@ -61,8 +61,9 @@ typedef struct /*_rtpmidi_peer*/ {
 	socklen_t dest_len;
 	//uint32_t ssrc;
 	uint8_t active; //marked for reuse
-	uint8_t learned; //learned / configured peer
+	uint8_t learned; //learned / configured peer (learned peers are marked inactive on session shutdown)
 	uint8_t connected; //currently in active session
+	ssize_t invite; //invite-list index for apple-mode learned peers
 } rtpmidi_peer;
 
 typedef struct /*_rtmidi_instance_data*/ {
@@ -98,14 +99,14 @@ typedef struct /*_rtpmidi_addr*/ {
 	uint8_t addr[sizeof(struct sockaddr_storage)];
 } rtpmidi_addr;
 
-enum applemidi_command {
+typedef enum {
 	apple_invite = 0x494E, //IN
 	apple_accept = 0x4F4B, //OK
 	apple_reject = 0x4E4F, //NO
 	apple_leave = 0x4259, //BY
 	apple_sync = 0x434B, //CK
 	apple_feedback = 0x5253 //RS
-};
+} applemidi_command;
 
 typedef struct /*_dns_name*/ {
 	size_t alloc;
-- 
cgit v1.2.3