diff options
author | Mke Brady <mikebrady@eircom.net> | 2015-06-01 23:16:50 +0100 |
---|---|---|
committer | Mke Brady <mikebrady@eircom.net> | 2015-06-01 23:16:50 +0100 |
commit | 87a0475c16a8203c9029a34e60cec00e23a86ab7 (patch) | |
tree | c454ba62ad2d964f7fde8481774115662cc3587f | |
parent | 04e27cfe4f37f411612e44a7af939ec5848f35d6 (diff) |
Reformat using clang-format.
-rw-r--r-- | audio.c | 40 | ||||
-rw-r--r-- | audio.h | 54 | ||||
-rw-r--r-- | audio_alsa.c | 355 | ||||
-rw-r--r-- | audio_ao.c | 159 | ||||
-rw-r--r-- | audio_dummy.c | 50 | ||||
-rw-r--r-- | audio_pipe.c | 83 | ||||
-rw-r--r-- | audio_pulse.c | 157 | ||||
-rw-r--r-- | audio_sndio.c | 80 | ||||
-rw-r--r-- | common.c | 595 | ||||
-rw-r--r-- | common.h | 105 | ||||
-rw-r--r-- | mdns.c | 99 | ||||
-rw-r--r-- | mdns.h | 18 | ||||
-rw-r--r-- | mdns_avahi.c | 189 | ||||
-rw-r--r-- | mdns_dns_sd.c | 103 | ||||
-rw-r--r-- | mdns_external.c | 205 | ||||
-rw-r--r-- | mdns_tinysvcmdns.c | 200 | ||||
-rw-r--r-- | player.c | 1109 | ||||
-rw-r--r-- | player.h | 6 | ||||
-rw-r--r-- | rtp.c | 944 | ||||
-rw-r--r-- | rtp.h | 7 | ||||
-rw-r--r-- | rtsp.c | 2029 | ||||
-rw-r--r-- | rtsp.h | 2 | ||||
-rw-r--r-- | shairport.c | 1178 | ||||
-rw-r--r-- | tinysvcmdns.c | 2429 | ||||
-rw-r--r-- | tinysvcmdns.h | 156 |
25 files changed, 5169 insertions, 5183 deletions
@@ -56,36 +56,32 @@ static audio_output *outputs[] = { #ifdef CONFIG_AO &audio_ao, #endif - &audio_dummy, - &audio_pipe, - NULL -}; - + &audio_dummy, &audio_pipe, NULL}; audio_output *audio_get_output(char *name) { - audio_output **out; + audio_output **out; - // default to the first - if (!name) - return outputs[0]; + // default to the first + if (!name) + return outputs[0]; - for (out=outputs; *out; out++) - if (!strcasecmp(name, (*out)->name)) - return *out; + for (out = outputs; *out; out++) + if (!strcasecmp(name, (*out)->name)) + return *out; - return NULL; + return NULL; } void audio_ls_outputs(void) { - audio_output **out; + audio_output **out; - printf("Available audio outputs:\n"); - for (out=outputs; *out; out++) - printf(" %s%s\n", (*out)->name, out==outputs ? " (default)" : ""); + printf("Available audio outputs:\n"); + for (out = outputs; *out; out++) + printf(" %s%s\n", (*out)->name, out == outputs ? " (default)" : ""); - for (out=outputs; *out; out++) { - printf("\n"); - printf("Options for output %s:\n", (*out)->name); - (*out)->help(); - } + for (out = outputs; *out; out++) { + printf("\n"); + printf("Options for output %s:\n", (*out)->name); + (*out)->help(); + } } @@ -15,33 +15,33 @@ typedef struct { } audio_parameters; typedef struct { - void (*help)(void); - char *name; - - // start of program - int (*init)(int argc, char **argv); - // at end of program - void (*deinit)(void); - - void (*start)(int sample_rate); - - // block of samples - void (*play)(short buf[], int samples); - void (*stop)(void); - - // may be null if not implemented - void (*flush)(void); - - // returns the delay before the next frame to be sent to the device would actually be audible. - // almost certainly wrong if the buffer is empty, so put silent buffers into it to make it busy. - // will change dynamically, so keep watching it. Implemented in ALSA only. - uint32_t (*delay)(); - - // may be NULL, in which case soft volume is applied - void (*volume)(double vol); - - // may be NULL, in which case soft volume parameters are used - void (*parameters)(audio_parameters* info); + void (*help)(void); + char *name; + + // start of program + int (*init)(int argc, char **argv); + // at end of program + void (*deinit)(void); + + void (*start)(int sample_rate); + + // block of samples + void (*play)(short buf[], int samples); + void (*stop)(void); + + // may be null if not implemented + void (*flush)(void); + + // returns the delay before the next frame to be sent to the device would actually be audible. + // almost certainly wrong if the buffer is empty, so put silent buffers into it to make it busy. + // will change dynamically, so keep watching it. Implemented in ALSA only. + uint32_t (*delay)(); + + // may be NULL, in which case soft volume is applied + void (*volume)(double vol); + + // may be NULL, in which case soft volume parameters are used + void (*parameters)(audio_parameters *info); } audio_output; audio_output *audio_get_output(char *name); diff --git a/audio_alsa.c b/audio_alsa.c index 257497f..02af8d8 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -43,9 +43,9 @@ static void stop(void); static void flush(void); static uint32_t delay(void); static void volume(double vol); -static void parameters(audio_parameters* info); -static int has_mute=0; -static int has_db_vol=0; +static void parameters(audio_parameters *info); +static int has_mute = 0; +static int has_db_vol = 0; static double set_volume; audio_output audio_alsa = { @@ -58,7 +58,7 @@ audio_output audio_alsa = { .flush = &flush, .delay = &delay, .play = &play, - .volume = NULL, // to be set later on... + .volume = NULL, // to be set later on... .parameters = NULL // to be set later on... }; @@ -81,16 +81,15 @@ static char *alsa_mix_ctrl = "Master"; static int alsa_mix_index = 0; static int play_number; -static int64_t accumulated_delay,accumulated_da_delay; +static int64_t accumulated_delay, accumulated_da_delay; static void help(void) { - printf(" -d output-device set the output device [default*|...]\n" - " -t mixer-type set the mixer type [software*|hardware]\n" - " -m mixer-device set the mixer device ['output-device'*|...]\n" - " -c mixer-control set the mixer control [Master*|...]\n" - " -i mixer-index set the mixer index [0*|...]\n" - " *) default option\n" - ); + printf(" -d output-device set the output device [default*|...]\n" + " -t mixer-type set the mixer type [software*|hardware]\n" + " -m mixer-device set the mixer device ['output-device'*|...]\n" + " -c mixer-control set the mixer control [Master*|...]\n" + " -i mixer-index set the mixer index [0*|...]\n" + " *) default option\n"); } static int init(int argc, char **argv) { @@ -100,52 +99,55 @@ static int init(int argc, char **argv) { int hardware_mixer = 0; config.audio_backend_buffer_desired_length = 6615; // this is the default for ALSA - config.audio_backend_latency_offset = 0; // this is the default for ALSA - + config.audio_backend_latency_offset = 0; // this is the default for ALSA + // get settings from settings file first, allow them to be over-ridden by command line options - - if (config.cfg!=NULL) { - /* Get the desired buffer size setting. */ - if(config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length", &value)) { - if ((value<0) || (value>66150)) - die("Invalid alsa audio backend buffer desired length \"%sd\". It should be between 0 and 66150, default is 6615",value); - else - config.audio_backend_buffer_desired_length=value; - } - - /* Get the latency offset. */ - if(config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) { - if ((value<-22050) || (value>22050)) - die("Invalid alsa audio backend buffer latency offset \"%sd\". It should be between -22050 and +22050, default is 0",value); - else - config.audio_backend_latency_offset=value; - } - /* Get the Output Device Name. */ - if(config_lookup_string(config.cfg, "alsa.output_device", &str)) { - alsa_out_dev = (char*)str; - } - - /* Get the Mixer Type setting. */ - if(config_lookup_string(config.cfg, "alsa.mixer_type", &str)) { - if (strcasecmp(str,"software")==0) - hardware_mixer=0; - else if (strcasecmp(str,"hardware")==0) - hardware_mixer=1; - else - die("Invalid alsa mixer option choice \"%s\". It should be \"software\" or \"hardware\""); - } + if (config.cfg != NULL) { + /* Get the desired buffer size setting. */ + if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length", &value)) { + if ((value < 0) || (value > 66150)) + die("Invalid alsa audio backend buffer desired length \"%sd\". It should be between 0 and " + "66150, default is 6615", + value); + else + config.audio_backend_buffer_desired_length = value; + } - /* Get the Mixer Device Name. */ - if(config_lookup_string(config.cfg, "alsa.mixer_device", &str)) { - alsa_mix_dev = (char*)str; - } - - /* Get the Mixer Control Name. */ - if(config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) { - alsa_mix_ctrl = (char*)str; - } + /* Get the latency offset. */ + if (config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) { + if ((value < -22050) || (value > 22050)) + die("Invalid alsa audio backend buffer latency offset \"%sd\". It should be between -22050 " + "and +22050, default is 0", + value); + else + config.audio_backend_latency_offset = value; + } + + /* Get the Output Device Name. */ + if (config_lookup_string(config.cfg, "alsa.output_device", &str)) { + alsa_out_dev = (char *)str; + } + + /* Get the Mixer Type setting. */ + if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) { + if (strcasecmp(str, "software") == 0) + hardware_mixer = 0; + else if (strcasecmp(str, "hardware") == 0) + hardware_mixer = 1; + else + die("Invalid alsa mixer option choice \"%s\". It should be \"software\" or \"hardware\""); + } + + /* Get the Mixer Device Name. */ + if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) { + alsa_mix_dev = (char *)str; + } + /* Get the Mixer Control Name. */ + if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) { + alsa_mix_ctrl = (char *)str; + } } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour @@ -154,37 +156,37 @@ static int init(int argc, char **argv) { // some platforms apparently require optreset = 1; - which? int opt; while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) { - switch (opt) { - case 'd': - alsa_out_dev = optarg; - break; - case 't': - if (strcmp(optarg, "hardware") == 0) - hardware_mixer = 1; - break; - case 'm': - alsa_mix_dev = optarg; - break; - case 'c': - alsa_mix_ctrl = optarg; - break; - case 'i': - alsa_mix_index = strtol(optarg, NULL, 10); - break; - default: - help(); - die("Invalid audio option -%c specified", opt); - } + switch (opt) { + case 'd': + alsa_out_dev = optarg; + break; + case 't': + if (strcmp(optarg, "hardware") == 0) + hardware_mixer = 1; + break; + case 'm': + alsa_mix_dev = optarg; + break; + case 'c': + alsa_mix_ctrl = optarg; + break; + case 'i': + alsa_mix_index = strtol(optarg, NULL, 10); + break; + default: + help(); + die("Invalid audio option -%c specified", opt); + } } if (optind < argc) - die("Invalid audio argument: %s", argv[optind]); - + die("Invalid audio argument: %s", argv[optind]); + if (!hardware_mixer) - return 0; + return 0; if (alsa_mix_dev == NULL) - alsa_mix_dev = alsa_out_dev; + alsa_mix_dev = alsa_out_dev; int ret = 0; @@ -193,39 +195,41 @@ static int init(int argc, char **argv) { snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl); if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0) - die ("Failed to open mixer"); + die("Failed to open mixer"); if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) - die ("Failed to attach mixer"); + die("Failed to attach mixer"); if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) - die ("Failed to register mixer element"); + die("Failed to register mixer element"); ret = snd_mixer_load(alsa_mix_handle); if (ret < 0) - die ("Failed to load mixer element"); + die("Failed to load mixer element"); alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid); if (!alsa_mix_elem) - die ("Failed to find mixer element"); - if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv)<0) - debug(1,"Can't read mixer's [linear] min and max volumes."); + die("Failed to find mixer element"); + if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < 0) + debug(1, "Can't read mixer's [linear] min and max volumes."); else { - if ((snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_minv,&alsa_mix_mindb)==0) && - (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_maxv,&alsa_mix_maxdb)==0)) { - has_db_vol=1; + if ((snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv, &alsa_mix_mindb) == 0) && + (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_maxv, &alsa_mix_maxdb) == 0)) { + has_db_vol = 1; audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff audio_alsa.parameters = ¶meters; // likewise the parameters stuff - if (alsa_mix_mindb==-9999999) { + if (alsa_mix_mindb == -9999999) { // trying to say that the lowest vol is mute, maybe? Raspberry Pi does this - if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_minv+1,&alsa_mix_mindb)==0) - debug(1,"Can't get dB value corresponding to a \"volume\" of 1."); + if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1, + &alsa_mix_mindb) == 0) + debug(1, "Can't get dB value corresponding to a \"volume\" of 1."); } - debug(1,"Hardware mixer has dB volume from %f to %f.",(1.0*alsa_mix_mindb)/100.0,(1.0*alsa_mix_maxdb)/100.0); + debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0, + (1.0 * alsa_mix_maxdb) / 100.0); } else { - debug(1,"Hardware mixer does not have dB volume -- not used."); + debug(1, "Hardware mixer does not have dB volume -- not used."); } } if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) { - has_mute=1; - debug(1,"Has mute ability."); + has_mute = 1; + debug(1, "Has mute ability."); } return 0; } @@ -233,20 +237,20 @@ static int init(int argc, char **argv) { static void deinit(void) { stop(); if (alsa_mix_handle) { - snd_mixer_close(alsa_mix_handle); + snd_mixer_close(alsa_mix_handle); } } -int open_alsa_device(void) { +int open_alsa_device(void) { int ret, dir = 0; unsigned int my_sample_rate = desired_sample_rate; - snd_pcm_uframes_t frames = 441*10; - snd_pcm_uframes_t buffer_size = frames*4; + snd_pcm_uframes_t frames = 441 * 10; + snd_pcm_uframes_t buffer_size = frames * 4; ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); if (ret < 0) - return(ret); - // die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret)); + return (ret); + // die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret)); snd_pcm_hw_params_alloca(&alsa_params); snd_pcm_hw_params_any(alsa_handle, alsa_params); @@ -260,75 +264,79 @@ int open_alsa_device(void) { if (ret < 0) { die("unable to set hw parameters: %s.", snd_strerror(ret)); } - if (my_sample_rate!=desired_sample_rate) { - die("Can't set the D/A converter to %d -- set to %d instead./n",desired_sample_rate,my_sample_rate); + if (my_sample_rate != desired_sample_rate) { + die("Can't set the D/A converter to %d -- set to %d instead./n", desired_sample_rate, + my_sample_rate); } - return(0); + return (0); } static void start(int sample_rate) { if (sample_rate != 44100) - die("Unexpected sample rate %d -- only 44,100 supported!",sample_rate); + die("Unexpected sample rate %d -- only 44,100 supported!", sample_rate); desired_sample_rate = sample_rate; // must be a variable } static uint32_t delay() { - if (alsa_handle==NULL) { - return 0; - } else { - pthread_mutex_lock(&alsa_mutex); - snd_pcm_sframes_t current_avail,current_delay = 0; - int derr,ignore; - if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING) { - derr = snd_pcm_avail_delay(alsa_handle,¤t_avail,¤t_delay); - // current_avail not used - if (derr != 0) { - ignore = snd_pcm_recover(alsa_handle, derr, 0); - debug(1,"Error %d in delay(): %s. Delay reported is %d frames.", derr, snd_strerror(derr),current_delay); - current_delay=-1; - } - } else if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) { - current_delay=0; - } else { - if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_XRUN) - current_delay=0; - else { - current_delay=-1; - debug(1,"Error -- ALSA delay(): bad state: %d.",snd_pcm_state(alsa_handle)); - } - if (derr = snd_pcm_prepare(alsa_handle)) { - ignore = snd_pcm_recover(alsa_handle, derr, 0); - debug(1,"Error preparing after delay error: %s.", snd_strerror(derr)); - current_delay = -1; - } - } - pthread_mutex_unlock(&alsa_mutex); - return current_delay; + if (alsa_handle == NULL) { + return 0; + } else { + pthread_mutex_lock(&alsa_mutex); + snd_pcm_sframes_t current_avail, current_delay = 0; + int derr, ignore; + if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING) { + derr = snd_pcm_avail_delay(alsa_handle, ¤t_avail, ¤t_delay); + // current_avail not used + if (derr != 0) { + ignore = snd_pcm_recover(alsa_handle, derr, 0); + debug(1, "Error %d in delay(): %s. Delay reported is %d frames.", derr, snd_strerror(derr), + current_delay); + current_delay = -1; + } + } else if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) { + current_delay = 0; + } else { + if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN) + current_delay = 0; + else { + current_delay = -1; + debug(1, "Error -- ALSA delay(): bad state: %d.", snd_pcm_state(alsa_handle)); + } + if (derr = snd_pcm_prepare(alsa_handle)) { + ignore = snd_pcm_recover(alsa_handle, derr, 0); + debug(1, "Error preparing after delay error: %s.", snd_strerror(derr)); + current_delay = -1; + } + } + pthread_mutex_unlock(&alsa_mutex); + return current_delay; } } static void play(short buf[], int samples) { int ret = 0; - if (alsa_handle==NULL) { - ret = open_alsa_device(); - if ((ret==0) && (audio_alsa.volume)) - volume(set_volume); - } - if (ret==0) { + if (alsa_handle == NULL) { + ret = open_alsa_device(); + if ((ret == 0) && (audio_alsa.volume)) + volume(set_volume); + } + if (ret == 0) { pthread_mutex_lock(&alsa_mutex); snd_pcm_sframes_t current_delay = 0; - int err,ignore; - if ((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)) { - err = snd_pcm_writei(alsa_handle, (char*)buf, samples); + int err, ignore; + if ((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) || + (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)) { + err = snd_pcm_writei(alsa_handle, (char *)buf, samples); if (err < 0) { ignore = snd_pcm_recover(alsa_handle, err, 0); - debug(1,"Error %d writing %d samples in play() %s.",err,samples, snd_strerror(err)); + debug(1, "Error %d writing %d samples in play() %s.", err, samples, snd_strerror(err)); } } else { - debug(1,"Error -- ALSA device in incorrect state (%d) for play.",snd_pcm_state(alsa_handle)); + debug(1, "Error -- ALSA device in incorrect state (%d) for play.", + snd_pcm_state(alsa_handle)); if (err = snd_pcm_prepare(alsa_handle)) { ignore = snd_pcm_recover(alsa_handle, err, 0); - debug(1,"Error preparing after play error: %s.", snd_strerror(err)); + debug(1, "Error preparing after play error: %s.", snd_strerror(err)); } } pthread_mutex_unlock(&alsa_mutex); @@ -340,20 +348,21 @@ static void flush(void) { if (alsa_handle) { // debug(1,"Dropping frames for flush..."); if (derr = snd_pcm_drop(alsa_handle)) - debug(1,"Error dropping frames: %s.", snd_strerror(derr)); + debug(1, "Error dropping frames: %s.", snd_strerror(derr)); // debug(1,"Dropped frames ok. State is %d.",snd_pcm_state(alsa_handle)); if (derr = snd_pcm_prepare(alsa_handle)) - debug(1,"Error preparing after flush: %s.", snd_strerror(derr)); + debug(1, "Error preparing after flush: %s.", snd_strerror(derr)); // debug(1,"Frames successfully dropped."); /* if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) - debug(1,"Flush returns to SND_PCM_STATE_PREPARED state."); + debug(1,"Flush returns to SND_PCM_STATE_PREPARED state."); if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING) debug(1,"Flush returns to SND_PCM_STATE_RUNNING state."); - */ - if (!((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING))) - debug(1,"Flush returning unexpected state -- %d.",snd_pcm_state(alsa_handle)); - + */ + if (!((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) || + (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING))) + debug(1, "Flush returning unexpected state -- %d.", snd_pcm_state(alsa_handle)); + // flush also closes the device snd_pcm_close(alsa_handle); alsa_handle = NULL; @@ -361,29 +370,29 @@ static void flush(void) { } static void stop(void) { - if (alsa_handle!=0) - // when we want to stop, we want the alsa device - // to be closed immediately -- we may even be killing the thread, so we don't wish to wait - // so we should flush first - flush(); // flush will also close the device - // close_alsa_device(); + if (alsa_handle != 0) + // when we want to stop, we want the alsa device + // to be closed immediately -- we may even be killing the thread, so we don't wish to wait + // so we should flush first + flush(); // flush will also close the device + // close_alsa_device(); } -static void parameters(audio_parameters* info) { - info->has_true_mute=has_mute; +static void parameters(audio_parameters *info) { + info->has_true_mute = has_mute; info->is_muted = ((has_mute) && (set_volume == -144.0)); - info->minimum_volume_dB=alsa_mix_mindb; - info->maximum_volume_dB=alsa_mix_maxdb; - info->airplay_volume=set_volume; - info->current_volume_dB=vol2attn(set_volume,alsa_mix_maxdb,alsa_mix_mindb); + info->minimum_volume_dB = alsa_mix_mindb; + info->maximum_volume_dB = alsa_mix_maxdb; + info->airplay_volume = set_volume; + info->current_volume_dB = vol2attn(set_volume, alsa_mix_maxdb, alsa_mix_mindb); } static void volume(double vol) { - set_volume=vol; - double vol_setting = vol2attn(vol,alsa_mix_maxdb,alsa_mix_mindb); + set_volume = vol; + double vol_setting = vol2attn(vol, alsa_mix_maxdb, alsa_mix_mindb); // debug(1,"Setting volume db to %f, for volume input of %f.",vol_setting/100,vol); if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol_setting, -1) != 0) - die ("Failed to set playback dB volume"); - if (has_mute) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol!=-144.0)); + die("Failed to set playback dB volume"); + if (has_mute) + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0)); } @@ -24,7 +24,6 @@ * OTHER DEALINGS IN THE SOFTWARE. */ - #include <stdio.h> #include <unistd.h> #include <memory.h> @@ -35,102 +34,96 @@ ao_device *dev = NULL; static void help(void) { - printf(" -d driver set the output driver\n" - " -o name=value set an arbitrary ao option\n" - " -i id shorthand for -o id=<id>\n" - " -n name shorthand for -o dev=<name> -o dsp=<name>\n" - ); + printf(" -d driver set the output driver\n" + " -o name=value set an arbitrary ao option\n" + " -i id shorthand for -o id=<id>\n" + " -n name shorthand for -o dev=<name> -o dsp=<name>\n"); } static int init(int argc, char **argv) { - ao_initialize(); - int driver = ao_default_driver_id(); - ao_option *ao_opts = NULL; - - optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour - argv--; // so we shift the arguments to satisfy getopt() - argc++; - - config.audio_backend_buffer_desired_length = 44100; // one second. - config.audio_backend_latency_offset = 0; - - // some platforms apparently require optreset = 1; - which? - int opt; - char *mid; - while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) { - switch (opt) { - case 'd': - driver = ao_driver_id(optarg); - if (driver < 0) - die("could not find ao driver %s", optarg); - break; - case 'i': - ao_append_option(&ao_opts, "id", optarg); - break; - case 'n': - ao_append_option(&ao_opts, "dev", optarg); - // Old libao versions (for example, 0.8.8) only support - // "dsp" instead of "dev". - ao_append_option(&ao_opts, "dsp", optarg); - break; - case 'o': - mid = strchr(optarg, '='); - if (!mid) - die("Expected an = in audio option %s", optarg); - *mid = 0; - ao_append_option(&ao_opts, optarg, mid+1); - break; - default: - help(); - die("Invalid audio option -%c specified", opt); - } + ao_initialize(); + int driver = ao_default_driver_id(); + ao_option *ao_opts = NULL; + + optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour + argv--; // so we shift the arguments to satisfy getopt() + argc++; + + config.audio_backend_buffer_desired_length = 44100; // one second. + config.audio_backend_latency_offset = 0; + + // some platforms apparently require optreset = 1; - which? + int opt; + char *mid; + while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) { + switch (opt) { + case 'd': + driver = ao_driver_id(optarg); + if (driver < 0) + die("could not find ao driver %s", optarg); + break; + case 'i': + ao_append_option(&ao_opts, "id", optarg); + break; + case 'n': + ao_append_option(&ao_opts, "dev", optarg); + // Old libao versions (for example, 0.8.8) only support + // "dsp" instead of "dev". + ao_append_option(&ao_opts, "dsp", optarg); + break; + case 'o': + mid = strchr(optarg, '='); + if (!mid) + die("Expected an = in audio option %s", optarg); + *mid = 0; + ao_append_option(&ao_opts, optarg, mid + 1); + break; + default: + help(); + die("Invalid audio option -%c specified", opt); } + } - if (optind < argc) - die("Invalid audio argument: %s", argv[optind]); + if (optind < argc) + die("Invalid audio argument: %s", argv[optind]); - ao_sample_format fmt; - memset(&fmt, 0, sizeof(fmt)); + ao_sample_format fmt; + memset(&fmt, 0, sizeof(fmt)); - fmt.bits = 16; - fmt.rate = 44100; - fmt.channels = 2; - fmt.byte_format = AO_FMT_NATIVE; + fmt.bits = 16; + fmt.rate = 44100; + fmt.channels = 2; + fmt.byte_format = AO_FMT_NATIVE; - dev = ao_open_live(driver, &fmt, ao_opts); + dev = ao_open_live(driver, &fmt, ao_opts); - return dev ? 0 : 1; + return dev ? 0 : 1; } static void deinit(void) { - if (dev) - ao_close(dev); - dev = NULL; - ao_shutdown(); + if (dev) + ao_close(dev); + dev = NULL; + ao_shutdown(); } static void start(int sample_rate) { - if (sample_rate != 44100) - die("unexpected sample rate!"); -} - -static void play(short buf[], int samples) { - ao_play(dev, (char*)buf, samples*4); -} - -static void stop(void) { + if (sample_rate != 44100) + die("unexpected sample rate!"); } -audio_output audio_ao = { - .name = "ao", - .help = &help, - .init = &init, - .deinit = &deinit, - .start = &start, - .stop = &stop, - .flush = NULL, - .delay = NULL, - .play = &play, - .volume = NULL, - .parameters = NULL -}; +static void play(short buf[], int samples) { ao_play(dev, (char *)buf, samples * 4); } + +static void stop(void) {} + +audio_output audio_ao = {.name = "ao", + .help = &help, + .init = &init, + .deinit = &deinit, + .start = &start, + .stop = &stop, + .flush = NULL, + .delay = NULL, + .play = &play, + .volume = NULL, + .parameters = NULL}; diff --git a/audio_dummy.c b/audio_dummy.c index 2fd8ebf..57fbec1 100644 --- a/audio_dummy.c +++ b/audio_dummy.c @@ -32,41 +32,31 @@ int Fs; long long starttime, samples_played; -static int init(int argc, char **argv) { - return 0; -} +static int init(int argc, char **argv) { return 0; } -static void deinit(void) { -} +static void deinit(void) {} static void start(int sample_rate) { - Fs = sample_rate; - starttime = 0; - samples_played = 0; - debug(1,"dummy audio output started at Fs=%d Hz\n", sample_rate); + Fs = sample_rate; + starttime = 0; + samples_played = 0; + debug(1, "dummy audio output started at Fs=%d Hz\n", sample_rate); } -static void play(short buf[], int samples) { -} +static void play(short buf[], int samples) {} -static void stop(void) { - debug(1,"dummy audio stopped\n"); -} +static void stop(void) { debug(1, "dummy audio stopped\n"); } -static void help(void) { - printf(" There are no options for dummy audio.\n"); -} +static void help(void) { printf(" There are no options for dummy audio.\n"); } -audio_output audio_dummy = { - .name = "dummy", - .help = &help, - .init = &init, - .deinit = &deinit, - .start = &start, - .stop = &stop, - .flush = NULL, - .delay = NULL, - .play = &play, - .volume = NULL, - .parameters = NULL -}; +audio_output audio_dummy = {.name = "dummy", + .help = &help, + .init = &init, + .deinit = &deinit, + .start = &start, + .stop = &stop, + .flush = NULL, + .delay = NULL, + .play = &play, + .volume = NULL, + .parameters = NULL}; diff --git a/audio_pipe.c b/audio_pipe.c index 00921e8..12b3336 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -38,76 +38,69 @@ static int fd = -1; char *pipename = NULL; static void start(int sample_rate) { - debug(1,"Pipename to start is \"%s\"",pipename); - if (strcasecmp(pipename,"STDOUT")==0) + debug(1, "Pipename to start is \"%s\"", pipename); + if (strcasecmp(pipename, "STDOUT") == 0) fd = STDOUT_FILENO; else fd = open(pipename, O_WRONLY); } -static void play(short buf[], int samples) { - int ignore = write(fd, buf, samples*4); -} +static void play(short buf[], int samples) { int ignore = write(fd, buf, samples * 4); } static void stop(void) { - if (fd!=STDOUT_FILENO) + if (fd != STDOUT_FILENO) close(fd); } static int init(int argc, char **argv) { - config.audio_backend_buffer_desired_length = 44100; // one second. + config.audio_backend_buffer_desired_length = 44100; // one second. config.audio_backend_latency_offset = 0; - if (config.cfg!=NULL) { - /* Get the Output Pipename. */ - const char *str; - if(config_lookup_string(config.cfg, "pipe.name", &str)) { - pipename = (char*)str; - } + if (config.cfg != NULL) { + /* Get the Output Pipename. */ + const char *str; + if (config_lookup_string(config.cfg, "pipe.name", &str)) { + pipename = (char *)str; + } } + if ((pipename == NULL) && (argc != 1)) + die("bad or missing argument(s) to pipe"); - if ((pipename==NULL) && (argc != 1)) - die("bad or missing argument(s) to pipe"); + if (argc == 1) + pipename = strdup(argv[0]); - if (argc==1) - pipename = strdup(argv[0]); - - - // here, create the pipe - if (strcasecmp(pipename,"STDOUT")!=0) - if (mkfifo(pipename, 0644) && errno != EEXIST) - die("Could not create metadata FIFO %s", pipename); + // here, create the pipe + if (strcasecmp(pipename, "STDOUT") != 0) + if (mkfifo(pipename, 0644) && errno != EEXIST) + die("Could not create metadata FIFO %s", pipename); - - debug(1,"Pipename is \"%s\"",pipename); + debug(1, "Pipename is \"%s\"", pipename); - // test open pipe so we error on startup if it's going to fail - start(44100); - stop(); + // test open pipe so we error on startup if it's going to fail + start(44100); + stop(); - return 0; + return 0; } static void deinit(void) { - if ((fd > 0) && (fd!=STDOUT_FILENO)) - close(fd); + if ((fd > 0) && (fd != STDOUT_FILENO)) + close(fd); } static void help(void) { - printf(" pipe takes 1 argument: the name of the FIFO to write to, which can be \"stdout\".\n"); + printf(" pipe takes 1 argument: the name of the FIFO to write to, which can be \"stdout\".\n"); } -audio_output audio_pipe = { - .name = "pipe", - .help = &help, - .init = &init, - .deinit = &deinit, - .start = &start, - .stop = &stop, - .flush = NULL, - .delay = NULL, - .play = &play, - .volume = NULL, - .parameters = NULL -}; +audio_output audio_pipe = {.name = "pipe", + .help = &help, + .init = &init, + .deinit = &deinit, + .start = &start, + .stop = &stop, + .flush = NULL, + .delay = NULL, + .play = &play, + .volume = NULL, + .parameters = NULL}; diff --git a/audio_pulse.c b/audio_pulse.c index d76c1de..29feed8 100644 --- a/audio_pulse.c +++ b/audio_pulse.c @@ -24,7 +24,6 @@ * OTHER DEALINGS IN THE SOFTWARE. */ - #include <stdio.h> #include <unistd.h> #include <memory.h> @@ -37,121 +36,105 @@ static pa_simple *pa_dev = NULL; static struct { - char *server; - char *sink; - char *apname; -} pulse_options = { - .server = NULL, - .sink = NULL, - .apname = NULL -}; + char *server; + char *sink; + char *apname; +} pulse_options = {.server = NULL, .sink = NULL, .apname = NULL}; static int pa_error; static void pulse_connect(void); static void help(void) { - printf(" -a server set the server name\n" - " -s sink set the output sink\n" - " -n name set the application name, as seen by PulseAudio\n" - " defaults to the access point name\n" - ); + printf(" -a server set the server name\n" + " -s sink set the output sink\n" + " -n name set the application name, as seen by PulseAudio\n" + " defaults to the access point name\n"); } static int init(int argc, char **argv) { - pulse_options.apname = config.apname; - - config.audio_backend_buffer_desired_length = 44100; // one second. - config.audio_backend_latency_offset = 0; - - optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour - argv--; // so we shift the arguments to satisfy getopt() - argc++; - - // some platforms apparently require optreset = 1; - which? - int opt; - while ((opt = getopt(argc, argv, "a:s:n:")) > 0) { - switch (opt) { - case 'a': - pulse_options.server = optarg; - break; - case 's': - pulse_options.sink = optarg; - break; - case 'n': - pulse_options.apname = optarg; - break; - default: - help(); - die("Invalid audio option -%c specified", opt); - } + pulse_options.apname = config.apname; + + config.audio_backend_buffer_desired_length = 44100; // one second. + config.audio_backend_latency_offset = 0; + + optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour + argv--; // so we shift the arguments to satisfy getopt() + argc++; + + // some platforms apparently require optreset = 1; - which? + int opt; + while ((opt = getopt(argc, argv, "a:s:n:")) > 0) { + switch (opt) { + case 'a': + pulse_options.server = optarg; + break; + case 's': + pulse_options.sink = optarg; + break; + case 'n': + pulse_options.apname = optarg; + break; + default: + help(); + die("Invalid audio option -%c specified", opt); } + } - if (optind < argc) - die("Invalid audio argument: %s", argv[optind]); + if (optind < argc) + die("Invalid audio argument: %s", argv[optind]); - pulse_connect(); + pulse_connect(); - return 0; + return 0; } static void pulse_connect(void) { - static const pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - - pa_dev = pa_simple_new(pulse_options.server, - pulse_options.apname, - PA_STREAM_PLAYBACK, - pulse_options.sink, - "Shairport Stream", - &ss, NULL, NULL, - &pa_error); - - if (!pa_dev) - die("Could not connect to pulseaudio server: %s", pa_strerror(pa_error)); + static const pa_sample_spec ss = {.format = PA_SAMPLE_S16LE, .rate = 44100, .channels = 2}; + + pa_dev = pa_simple_new(pulse_options.server, pulse_options.apname, PA_STREAM_PLAYBACK, + pulse_options.sink, "Shairport Stream", &ss, NULL, NULL, &pa_error); + + if (!pa_dev) + die("Could not connect to pulseaudio server: %s", pa_strerror(pa_error)); } static void deinit(void) { - if (pa_dev) - pa_simple_free(pa_dev); - pa_dev = NULL; + if (pa_dev) + pa_simple_free(pa_dev); + pa_dev = NULL; } static void start(int sample_rate) { - if (sample_rate != 44100) - die("unexpected sample rate!"); + if (sample_rate != 44100) + die("unexpected sample rate!"); } static void play(short buf[], int samples) { - if( pa_simple_write(pa_dev, (char *)buf, (size_t)samples * 4, &pa_error) < 0 ) { - fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(pa_error)); - if (pa_error == PA_ERR_CONNECTIONTERMINATED) { - fprintf(stderr, __FILE__": reconnecting."); - deinit(); - pulse_connect(); - } + if (pa_simple_write(pa_dev, (char *)buf, (size_t)samples * 4, &pa_error) < 0) { + fprintf(stderr, __FILE__ ": pa_simple_write() failed: %s\n", pa_strerror(pa_error)); + if (pa_error == PA_ERR_CONNECTIONTERMINATED) { + fprintf(stderr, __FILE__ ": reconnecting."); + deinit(); + pulse_connect(); } + } } static void stop(void) { - if (pa_simple_drain(pa_dev, &pa_error) < 0) - fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(pa_error)); + if (pa_simple_drain(pa_dev, &pa_error) < 0) + fprintf(stderr, __FILE__ ": pa_simple_drain() failed: %s\n", pa_strerror(pa_error)); } -audio_output audio_pulse = { - .name = "pulse", - .help = &help, - .init = &init, - .deinit = &deinit, - .start = &start, - .stop = &stop, - .flush = NULL, - .delay = NULL, - .play = &play, - .volume = NULL, - .parameters = NULL -}; +audio_output audio_pulse = {.name = "pulse", + .help = &help, + .init = &init, + .deinit = &deinit, + .start = &start, + .stop = &stop, + .flush = NULL, + .delay = NULL, + .play = &play, + .volume = NULL, + .parameters = NULL}; diff --git a/audio_sndio.c b/audio_sndio.c index 65a4277..e59dbb8 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -24,67 +24,61 @@ static struct sio_hdl *sio; static struct sio_par par; static int init(int argc, char **argv) { - sio = sio_open(SIO_DEVANY, SIO_PLAY, 0); - if (!sio) - die("sndio: cannot connect to sound server"); + sio = sio_open(SIO_DEVANY, SIO_PLAY, 0); + if (!sio) + die("sndio: cannot connect to sound server"); - sio_initpar(&par); + sio_initpar(&par); - par.bits = 16; - par.rate = 44100; - par.pchan = 2; - par.le = SIO_LE_NATIVE; - par.sig = 1; + par.bits = 16; + par.rate = 44100; + par.pchan = 2; + par.le = SIO_LE_NATIVE; + par.sig = 1; - if (!sio_setpar(sio, &par)) - die("sndio: failed to set audio parameters"); - if (!sio_getpar(sio, &par)) - die("sndio: failed to get audio parameters"); - - config.audio_backend_buffer_desired_length = 44100; // one second. + if (!sio_setpar(sio, &par)) + die("sndio: failed to set audio parameters"); + if (!sio_getpar(sio, &par)) + die("sndio: failed to get audio parameters"); + + config.audio_backend_buffer_desired_length = 44100; // one second. config.audio_backend_latency_offset = 0; - return 0; + return 0; } -static void deinit(void) { - sio_close(sio); -} +static void deinit(void) { sio_close(sio); } static void start(int sample_rate) { - if (sample_rate != par.rate) - die("unexpected sample rate!"); - sio_start(sio); + if (sample_rate != par.rate) + die("unexpected sample rate!"); + sio_start(sio); } static void play(short buf[], int samples) { - sio_write(sio, (char *)buf, samples * par.bps * par.pchan); + sio_write(sio, (char *)buf, samples * par.bps * par.pchan); } -static void stop(void) { - sio_stop(sio); -} +static void stop(void) { sio_stop(sio); } static void help(void) { - printf(" There are no options for sndio audio.\n"); - printf(" Use AUDIODEVICE environment variable.\n"); + printf(" There are no options for sndio audio.\n"); + printf(" Use AUDIODEVICE environment variable.\n"); } static void volume(double vol) { - unsigned int v = vol * SIO_MAXVOL; - sio_setvol(sio, v); + unsigned int v = vol * SIO_MAXVOL; + sio_setvol(sio, v); } -audio_output audio_sndio = { - .name = "sndio", - .help = &help, - .init = &init, - .deinit = &deinit, - .start = &start, - .stop = &stop, - .flush = NULL, - .delay = NULL, - .play = &play, - .volume = &volume, - .parameters = NULL -}; +audio_output audio_sndio = {.name = "sndio", + .help = &help, + .init = &init, + .deinit = &deinit, + .start = &start, + .stop = &stop, + .flush = NULL, + .delay = NULL, + .play = &play, + .volume = &volume, + .parameters = NULL}; @@ -64,12 +64,10 @@ #endif #endif - - #include "common.h" #include <libdaemon/dlog.h> -//true if Shairport Sync is supposed to be sending output to the output device, false otherwise +// true if Shairport Sync is supposed to be sending output to the output device, false otherwise static volatile int requested_connection_state_to_output = 1; @@ -77,99 +75,98 @@ shairport_cfg config; int debuglev = 0; -int get_requested_connection_state_to_output() { - return requested_connection_state_to_output; -} - -void set_requested_connection_state_to_output(int v) { - requested_connection_state_to_output = v; -} +int get_requested_connection_state_to_output() { return requested_connection_state_to_output; } +void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; } void die(char *format, ...) { - char s[1024]; - s[0]=0; - va_list args; - va_start(args, format); - vsprintf(s,format,args); - va_end(args); - daemon_log(LOG_EMERG,"%s", s); - shairport_shutdown(); - exit(1); + char s[1024]; + s[0] = 0; + va_list args; + va_start(args, format); + vsprintf(s, format, args); + va_end(args); + daemon_log(LOG_EMERG, "%s", s); + shairport_shutdown(); + exit(1); } void warn(char *format, ...) { - char s[1024]; - s[0]=0; - va_list args; - va_start(args, format); - vsprintf(s,format,args); - va_end(args); - daemon_log(LOG_WARNING,"%s", s); + char s[1024]; + s[0] = 0; + va_list args; + va_start(args, format); + vsprintf(s, format, args); + va_end(args); + daemon_log(LOG_WARNING, "%s", s); } void debug(int level, char *format, ...) { - if (level > debuglev) - return; - char s[1024]; - s[0]=0; - va_list args; - va_start(args, format); - vsprintf(s,format,args); - va_end(args); - daemon_log(LOG_DEBUG,"%s", s); + if (level > debuglev) + return; + char s[1024]; + s[0] = 0; + va_list args; + va_start(args, format); + vsprintf(s, format, args); + va_end(args); + daemon_log(LOG_DEBUG, "%s", s); } void inform(char *format, ...) { - char s[1024]; - s[0]=0; - va_list args; - va_start(args, format); - vsprintf(s,format,args); - va_end(args); - daemon_log(LOG_INFO,"%s", s); + char s[1024]; + s[0] = 0; + va_list args; + va_start(args, format); + vsprintf(s, format, args); + va_end(args); + daemon_log(LOG_INFO, "%s", s); } #ifdef HAVE_LIBPOLARSSL char *base64_enc(uint8_t *input, int length) { char *buf = NULL; size_t dlen = 0; - int rc = base64_encode(NULL,&dlen,input,length); - if (rc && (rc!=POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL)) - debug(1,"Error %d getting length of base64 encode.",rc); + int rc = base64_encode(NULL, &dlen, input, length); + if (rc && (rc != POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL)) + debug(1, "Error %d getting length of base64 encode.", rc); else { buf = (char *)malloc(dlen); - rc = base64_encode((unsigned char *)buf,&dlen,input,length); - if (rc!=0) - debug(1,"Error %d encoding base64.",rc); + rc = base64_encode((unsigned char *)buf, &dlen, input, length); + if (rc != 0) + debug(1, "Error %d encoding base64.", rc); } return buf; } uint8_t *base64_dec(char *input, int *outlen) { - // slight problem here is that Apple cut the padding off their challenges. We must restore it before passing it in to the decoder, it seems + // slight problem here is that Apple cut the padding off their challenges. We must restore it + // before passing it in to the decoder, it seems uint8_t *buf = NULL; size_t dlen = 0; - int inbufsize = ((strlen(input)+3)/4)*4; // this is the size of the input buffer we will send to the decoder, but we need space for 3 extra "="s and a NULL - char *inbuf = malloc(inbufsize+4); - if (inbuf==0) - debug(1,"Can't malloc memory for inbuf in base64_decode."); + int inbufsize = ((strlen(input) + 3) / 4) * 4; // this is the size of the input buffer we will + // send to the decoder, but we need space for 3 + // extra "="s and a NULL + char *inbuf = malloc(inbufsize + 4); + if (inbuf == 0) + debug(1, "Can't malloc memory for inbuf in base64_decode."); else { - strcpy(inbuf,input); - strcat(inbuf,"==="); - // debug(1,"base64_dec called with string \"%s\", length %d, filled string: \"%s\", length %d.",input,strlen(input),inbuf,inbufsize); - int rc = base64_decode(buf,&dlen,(unsigned char *)inbuf,inbufsize); - if (rc && (rc!=POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL)) - debug(1,"Error %d getting decode length, result is %d.",rc,dlen); + strcpy(inbuf, input); + strcat(inbuf, "==="); + // debug(1,"base64_dec called with string \"%s\", length %d, filled string: \"%s\", length + // %d.",input,strlen(input),inbuf,inbufsize); + int rc = base64_decode(buf, &dlen, (unsigned char *)inbuf, inbufsize); + if (rc && (rc != POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL)) + debug(1, "Error %d getting decode length, result is %d.", rc, dlen); else { // debug(1,"Decode size is %d.",dlen); buf = malloc(dlen); - if (buf==0) - debug(1,"Can't allocate memory in base64_dec."); + if (buf == 0) + debug(1, "Can't allocate memory in base64_dec."); else { - rc = base64_decode(buf,&dlen,(unsigned char *)inbuf,inbufsize); - if (rc!=0) - debug(1,"Error %d in base64_dec.",rc); + rc = base64_decode(buf, &dlen, (unsigned char *)inbuf, inbufsize); + if (rc != 0) + debug(1, "Error %d in base64_dec.", rc); } } free(inbuf); @@ -181,221 +178,226 @@ uint8_t *base64_dec(char *input, int *outlen) { #ifdef HAVE_LIBSSL char *base64_enc(uint8_t *input, int length) { - BIO *bmem, *b64; - BUF_MEM *bptr; - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - BIO_write(b64, input, length); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - char *buf = (char *)malloc(bptr->length); - if (bptr->length) { - memcpy(buf, bptr->data, bptr->length-1); - buf[bptr->length-1] = 0; - } + BIO *bmem, *b64; + BUF_MEM *bptr; + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_write(b64, input, length); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + char *buf = (char *)malloc(bptr->length); + if (bptr->length) { + memcpy(buf, bptr->data, bptr->length - 1); + buf[bptr->length - 1] = 0; + } - BIO_free_all(bmem); + BIO_free_all(bmem); - return buf; + return buf; } uint8_t *base64_dec(char *input, int *outlen) { - BIO *bmem, *b64; - int inlen = strlen(input); + BIO *bmem, *b64; + int inlen = strlen(input); - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); - // Apple cut the padding off their challenges; restore it - BIO_write(bmem, input, inlen); - while (inlen++ & 3) - BIO_write(bmem, "=", 1); - BIO_flush(bmem); + // Apple cut the padding off their challenges; restore it + BIO_write(bmem, input, inlen); + while (inlen++ & 3) + BIO_write(bmem, "=", 1); + BIO_flush(bmem); - int bufsize = strlen(input)*3/4 + 1; - uint8_t *buf = malloc(bufsize); - int nread; + int bufsize = strlen(input) * 3 / 4 + 1; + uint8_t *buf = malloc(bufsize); + int nread; - nread = BIO_read(b64, buf, bufsize); + nread = BIO_read(b64, buf, bufsize); - BIO_free_all(bmem); + BIO_free_all(bmem); - *outlen = nread; - return buf; + *outlen = nread; + return buf; } #endif static char super_secret_key[] = -"-----BEGIN RSA PRIVATE KEY-----\n" -"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n" -"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n" -"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n" -"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n" -"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n" -"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n" -"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n" -"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n" -"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n" -"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n" -"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n" -"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n" -"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n" -"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n" -"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n" -"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n" -"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n" -"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n" -"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n" -"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n" -"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n" -"-----END RSA PRIVATE KEY-----\0"; + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n" + "wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n" + "wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n" + "/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n" + "UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n" + "BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n" + "LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n" + "NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n" + "lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n" + "aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n" + "a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n" + "oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n" + "oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n" + "k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n" + "AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n" + "cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n" + "54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n" + "17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n" + "1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n" + "LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n" + "2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n" + "-----END RSA PRIVATE KEY-----\0"; #ifdef HAVE_LIBSSL uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) { -static RSA *rsa = NULL; - - if (!rsa) { - BIO *bmem = BIO_new_mem_buf(super_secret_key, -1); - rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL); - BIO_free(bmem); - } - - uint8_t *out = malloc(RSA_size(rsa)); - switch (mode) { - case RSA_MODE_AUTH: - *outlen = RSA_private_encrypt(inlen, input, out, rsa, - RSA_PKCS1_PADDING); - break; - case RSA_MODE_KEY: - *outlen = RSA_private_decrypt(inlen, input, out, rsa, - RSA_PKCS1_OAEP_PADDING); - break; - default: - die("bad rsa mode"); - } - return out; + static RSA *rsa = NULL; + + if (!rsa) { + BIO *bmem = BIO_new_mem_buf(super_secret_key, -1); + rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL); + BIO_free(bmem); + } + + uint8_t *out = malloc(RSA_size(rsa)); + switch (mode) { + case RSA_MODE_AUTH: + *outlen = RSA_private_encrypt(inlen, input, out, rsa, RSA_PKCS1_PADDING); + break; + case RSA_MODE_KEY: + *outlen = RSA_private_decrypt(inlen, input, out, rsa, RSA_PKCS1_OAEP_PADDING); + break; + default: + die("bad rsa mode"); + } + return out; } #endif #ifdef HAVE_LIBPOLARSSL uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) { - rsa_context trsa; - const char *pers = "rsa_encrypt"; - int rc; - - entropy_context entropy; - ctr_drbg_context ctr_drbg; - entropy_init( &entropy ); - if( ( rc = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy,(const unsigned char *) pers,strlen( pers ) ) ) != 0 ) - debug(1, "ctr_drbg_init returned %d\n", rc ); - - rsa_init(&trsa,RSA_PKCS_V21,POLARSSL_MD_SHA1); // padding and hash id get overwritten - // BTW, this seems to reset a lot of parameters in the rsa_context - rc = x509parse_key(&trsa,(unsigned char *)super_secret_key,strlen(super_secret_key),NULL,0); - if (rc!=0) - debug(1,"Error %d reading the private key."); - - uint8_t *out = NULL; - - switch (mode) { - case RSA_MODE_AUTH: - trsa.padding = RSA_PKCS_V15; - trsa.hash_id = POLARSSL_MD_NONE; - debug(2,"rsa_apply encrypt"); - out = malloc(trsa.len); - rc = rsa_pkcs1_encrypt( &trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, inlen, input, out ); - if (rc!=0) - debug(1,"rsa_pkcs1_encrypt error %d.",rc); - *outlen=trsa.len; - break; - case RSA_MODE_KEY: - debug(2,"rsa_apply decrypt"); - trsa.padding=RSA_PKCS_V21; - trsa.hash_id=POLARSSL_MD_SHA1; - out = malloc(trsa.len); + rsa_context trsa; + const char *pers = "rsa_encrypt"; + int rc; + + entropy_context entropy; + ctr_drbg_context ctr_drbg; + entropy_init(&entropy); + if ((rc = ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (const unsigned char *)pers, + strlen(pers))) != 0) + debug(1, "ctr_drbg_init returned %d\n", rc); + + rsa_init(&trsa, RSA_PKCS_V21, POLARSSL_MD_SHA1); // padding and hash id get overwritten + // BTW, this seems to reset a lot of parameters in the rsa_context + rc = x509parse_key(&trsa, (unsigned char *)super_secret_key, strlen(super_secret_key), NULL, 0); + if (rc != 0) + debug(1, "Error %d reading the private key."); + + uint8_t *out = NULL; + + switch (mode) { + case RSA_MODE_AUTH: + trsa.padding = RSA_PKCS_V15; + trsa.hash_id = POLARSSL_MD_NONE; + debug(2, "rsa_apply encrypt"); + out = malloc(trsa.len); + rc = rsa_pkcs1_encrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, inlen, input, out); + if (rc != 0) + debug(1, "rsa_pkcs1_encrypt error %d.", rc); + *outlen = trsa.len; + break; + case RSA_MODE_KEY: + debug(2, "rsa_apply decrypt"); + trsa.padding = RSA_PKCS_V21; + trsa.hash_id = POLARSSL_MD_SHA1; + out = malloc(trsa.len); #if POLARSSL_VERSION_NUMBER >= 0x01020900 - rc = rsa_pkcs1_decrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, (size_t *)outlen, input, out, trsa.len); + rc = rsa_pkcs1_decrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, (size_t *)outlen, input, + out, trsa.len); #else - rc = rsa_pkcs1_decrypt(&trsa, RSA_PRIVATE, outlen, input, out, trsa.len); + rc = rsa_pkcs1_decrypt(&trsa, RSA_PRIVATE, outlen, input, out, trsa.len); #endif - if (rc!=0) - debug(1,"decrypt error %d.",rc); - break; - default: - die("bad rsa mode"); - } - rsa_free(&trsa); - debug(2,"rsa_apply exit"); - return out; + if (rc != 0) + debug(1, "decrypt error %d.", rc); + break; + default: + die("bad rsa mode"); + } + rsa_free(&trsa); + debug(2, "rsa_apply exit"); + return out; } #endif void command_start(void) { - if (config.cmd_start) { - /*Spawn a child to run the program.*/ - pid_t pid=fork(); - if (pid==0) { /* child process */ - int argC; - char **argV; - // debug(1,"on-start command found."); - if (poptParseArgvString(config.cmd_start,&argC,(const char ***)&argV)!=0) // note that argV should be free()'d after use, but we expect this fork to exit eventually. - debug(1,"Can't decipher on-start command arguments"); - else { - // debug(1,"Executing on-start command %s with %d arguments.",argV[0],argC); - execv(argV[0],argV); - warn("Execution of on-start command failed to start"); - debug(1,"Error executing on-start command %s",config.cmd_start); - exit(127); /* only if execv fails */ - } - } else { - if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */ - pid_t rc = waitpid(pid,0,0); /* wait for child to exit */ - if (rc!=pid) { - warn("Execution of on-start command returned an error."); - debug(1,"on-start command %s finished with error %d",config.cmd_start,errno); - } - } - // debug(1,"Continue after on-start command"); - } - } + if (config.cmd_start) { + /*Spawn a child to run the program.*/ + pid_t pid = fork(); + if (pid == 0) { /* child process */ + int argC; + char **argV; + // debug(1,"on-start command found."); + if (poptParseArgvString(config.cmd_start, &argC, (const char ***)&argV) != + 0) // note that argV should be free()'d after use, but we expect this fork to exit + // eventually. + debug(1, "Can't decipher on-start command arguments"); + else { + // debug(1,"Executing on-start command %s with %d arguments.",argV[0],argC); + execv(argV[0], argV); + warn("Execution of on-start command failed to start"); + debug(1, "Error executing on-start command %s", config.cmd_start); + exit(127); /* only if execv fails */ + } + } else { + if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for + process to finish */ + pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */ + if (rc != pid) { + warn("Execution of on-start command returned an error."); + debug(1, "on-start command %s finished with error %d", config.cmd_start, errno); + } + } + // debug(1,"Continue after on-start command"); + } + } } void command_stop(void) { - if (config.cmd_stop) { - /*Spawn a child to run the program.*/ - pid_t pid=fork(); - if (pid==0) { /* child process */ - int argC; - char **argV; - // debug(1,"on-stop command found."); - if (poptParseArgvString(config.cmd_stop,&argC,(const char ***)&argV)!=0) // note that argV should be free()'d after use, but we expect this fork to exit eventually. - debug(1,"Can't decipher on-stop command arguments"); + if (config.cmd_stop) { + /*Spawn a child to run the program.*/ + pid_t pid = fork(); + if (pid == 0) { /* child process */ + int argC; + char **argV; + // debug(1,"on-stop command found."); + if (poptParseArgvString(config.cmd_stop, &argC, (const char ***)&argV) != + 0) // note that argV should be free()'d after use, but we expect this fork to exit + // eventually. + debug(1, "Can't decipher on-stop command arguments"); else { - // debug(1,"Executing on-stop command %s",config.cmd_stop); - execv(argV[0],argV); - warn("Execution of on-stop command failed to start"); - debug(1,"Error executing on-stop command %s",config.cmd_stop); - exit(127); /* only if execv fails */ - } - } else { - if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */ - pid_t rc = waitpid(pid,0,0); /* wait for child to exit */ - if (rc!=pid) { - warn("Execution of on-stop command returned an error."); - debug(1,"Stop command %s finished with error %d",config.cmd_stop,errno); - } - } - // debug(1,"Continue after on-stop command"); - } - } + // debug(1,"Executing on-stop command %s",config.cmd_stop); + execv(argV[0], argV); + warn("Execution of on-stop command failed to start"); + debug(1, "Error executing on-stop command %s", config.cmd_stop); + exit(127); /* only if execv fails */ + } + } else { + if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for + process to finish */ + pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */ + if (rc != pid) { + warn("Execution of on-stop command returned an error."); + debug(1, "Stop command %s finished with error %d", config.cmd_stop, errno); + } + } + // debug(1,"Continue after on-stop command"); + } + } } - // this is for reading an unsigned 32 bit number, such as an RTP timestamp uint32_t uatoi(const char *nptr) { @@ -404,19 +406,25 @@ uint32_t uatoi(const char *nptr) { return r; } -// Given a volume (0 to -30) and high and low attenuations available in the mixer in dB, return an attenuation depending on the volume and the function's transfer function +// Given a volume (0 to -30) and high and low attenuations available in the mixer in dB, return an +// attenuation depending on the volume and the function's transfer function // See http://tangentsoft.net/audio/atten.html for data on good attenuators. -// We want a smooth attenuation function, like, for example, the ALPS RK27 Potentiometer transfer functions referred to at the link above. +// We want a smooth attenuation function, like, for example, the ALPS RK27 Potentiometer transfer +// functions referred to at the link above. // Note that the max_db and min_db are given as dB*100 -double vol2attn(double vol, long max_db, long min_db) { +double vol2attn(double vol, long max_db, long min_db) { -// We use a little coordinate geometry to build a transfer function from the volume passed in to the device's dynamic range. +// We use a little coordinate geometry to build a transfer function from the volume passed in to the +// device's dynamic range. // (See the diagram in the documents folder.) -// The x axis is the "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from the bottom of the range to the top. -// We build the transfer function from one or more lines. We characterise each line with two numbers: -// the first is where on x the line starts when y=0 (x can be from 0 to -30); the second is where on y the line stops when when x is -30. +// The x axis is the "volume in" which will be from -30 to 0. The y axis will be the "volume out" +// which will be from the bottom of the range to the top. +// We build the transfer function from one or more lines. We characterise each line with two +// numbers: +// the first is where on x the line starts when y=0 (x can be from 0 to -30); the second is where on +// y the line stops when when x is -30. // thus, if the line was characterised as {0,-30}, it would be an identity transfer. // Assuming, for example, a dynamic range of lv=-60 to hv=0 // Typically we'll use three lines -- a three order transfer function @@ -427,27 +435,33 @@ double vol2attn(double vol, long max_db, long min_db) { #define order 3 double vol_setting = max_db; - - if ((vol<=0.0) && (vol>=-30.0)) { - long range_db = max_db-min_db; // this will be a positive nunmber + + if ((vol <= 0.0) && (vol >= -30.0)) { + long range_db = max_db - min_db; // this will be a positive nunmber // debug(1,"Volume min %ddB, max %ddB, range %ddB.",min_db,max_db,range_db); - //double first_slope = -3000.0; // this is the slope of the attenuation at the high end -- 30dB for the full rotation. - double first_slope = -range_db/2; // this is the slope of the attenuation at the high end -- 30dB for the full rotation. - if (-range_db>first_slope) + // double first_slope = -3000.0; // this is the slope of the attenuation at the high end -- 30dB + // for the full rotation. + double first_slope = + -range_db / + 2; // this is the slope of the attenuation at the high end -- 30dB for the full rotation. + if (-range_db > first_slope) first_slope = range_db; - double lines[order][2] = {{0,first_slope},{-5,first_slope-(range_db+first_slope)/2},{-17,-range_db}}; + double lines[order][2] = { + {0, first_slope}, {-5, first_slope - (range_db + first_slope) / 2}, {-17, -range_db}}; int i; - for (i=0;i<order;i++) { - if (vol<=lines[i][0]) { - double tvol = lines[i][1]*(vol-lines[i][0])/(-30-lines[i][0]); - // debug(1,"On line %d, end point of %f, input vol %f yields output vol %f.",i,lines[i][1],vol,tvol); - if (tvol<vol_setting) - vol_setting=tvol; + for (i = 0; i < order; i++) { + if (vol <= lines[i][0]) { + double tvol = lines[i][1] * (vol - lines[i][0]) / (-30 - lines[i][0]); + // debug(1,"On line %d, end point of %f, input vol %f yields output vol + // %f.",i,lines[i][1],vol,tvol); + if (tvol < vol_setting) + vol_setting = tvol; } } - vol_setting+=max_db; - } else if (vol!=-144.0) { - debug(1,"Volume request value %f is out of range: should be from 0.0 to -30.0 or -144.0.",vol); + vol_setting += max_db; + } else if (vol != -144.0) { + debug(1, "Volume request value %f is out of range: should be from 0.0 to -30.0 or -144.0.", + vol); } else { vol_setting = min_db; // for safety, return the lowest setting... } @@ -456,39 +470,40 @@ double vol2attn(double vol, long max_db, long min_db) { } uint64_t get_absolute_time_in_fp() { - uint64_t time_now_fp; + uint64_t time_now_fp; #ifdef COMPILE_FOR_LINUX - struct timespec tn; - clock_gettime(CLOCK_MONOTONIC,&tn); - time_now_fp=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000; + struct timespec tn; + clock_gettime(CLOCK_MONOTONIC, &tn); + time_now_fp = ((uint64_t)tn.tv_sec << 32) + ((uint64_t)tn.tv_nsec << 32) / 1000000000; #endif #ifdef COMPILE_FOR_OSX - uint64_t time_now_mach; - uint64_t elapsedNano; - static mach_timebase_info_data_t sTimebaseInfo = {0,0}; + uint64_t time_now_mach; + uint64_t elapsedNano; + static mach_timebase_info_data_t sTimebaseInfo = {0, 0}; - time_now_mach = mach_absolute_time(); + time_now_mach = mach_absolute_time(); - // If this is the first time we've run, get the timebase. - // We can use denom == 0 to indicate that sTimebaseInfo is - // uninitialised because it makes no sense to have a zero - // denominator in a fraction. + // If this is the first time we've run, get the timebase. + // We can use denom == 0 to indicate that sTimebaseInfo is + // uninitialised because it makes no sense to have a zero + // denominator in a fraction. - if ( sTimebaseInfo.denom == 0 ) { - debug(1,"Mac initialise timebase info."); - (void) mach_timebase_info(&sTimebaseInfo); - } + if (sTimebaseInfo.denom == 0) { + debug(1, "Mac initialise timebase info."); + (void)mach_timebase_info(&sTimebaseInfo); + } + + // Do the maths. We hope that the multiplication doesn't + // overflow; the price you pay for working in fixed point. - // Do the maths. We hope that the multiplication doesn't - // overflow; the price you pay for working in fixed point. + // this gives us nanoseconds + uint64_t time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom; - // this gives us nanoseconds - uint64_t time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom; - - // take the units and shift them to the upper half of the fp, and take the nanoseconds, shift them to the upper half and then divide the result to 1000000000 - time_now_fp = ((time_now_ns/1000000000)<<32) + (((time_now_ns%1000000000)<<32)/1000000000); + // take the units and shift them to the upper half of the fp, and take the nanoseconds, shift them + // to the upper half and then divide the result to 1000000000 + time_now_fp = + ((time_now_ns / 1000000000) << 32) + (((time_now_ns % 1000000000) << 32) / 1000000000); #endif return time_now_fp; } - @@ -10,77 +10,79 @@ #include "mdns.h" #if defined(__APPLE__) && defined(__MACH__) - /* Apple OSX and iOS (Darwin). ------------------------------ */ +/* Apple OSX and iOS (Darwin). ------------------------------ */ #include <TargetConditionals.h> #if TARGET_OS_MAC == 1 - /* OSX */ - #define COMPILE_FOR_OSX 1 +/* OSX */ +#define COMPILE_FOR_OSX 1 #endif #endif #if defined(__linux__) - /* Linux. --------------------------------------------------- */ - #define COMPILE_FOR_LINUX 1 +/* Linux. --------------------------------------------------- */ +#define COMPILE_FOR_LINUX 1 #endif // struct sockaddr_in6 is bigger than struct sockaddr. derp #ifdef AF_INET6 - #define SOCKADDR struct sockaddr_storage - #define SAFAMILY ss_family +#define SOCKADDR struct sockaddr_storage +#define SAFAMILY ss_family #else - #define SOCKADDR struct sockaddr - #define SAFAMILY sa_family +#define SOCKADDR struct sockaddr +#define SAFAMILY sa_family #endif - enum stuffing_type { - ST_basic = 0, + ST_basic = 0, ST_soxr, } type; - - typedef struct { - config_t* cfg; - char *password; - char *apname; + config_t *cfg; + char *password; + char *apname; #ifdef CONFIG_METADATA - int metadata_enabled; - char *metadata_pipename; - int get_coverart; + int metadata_enabled; + char *metadata_pipename; + int get_coverart; #endif - uint8_t hw_addr[6]; - int port; - int ignore_volume_control; - int resyncthreshold; // if it get's out of whack my more than this, resync. Zero means never resync. - int allow_session_interruption; - int timeout; // while in play mode, exit if no packets of audio come in for more than this number of seconds . Zero means never exit. - int dont_check_timeout; // this is used to maintain backward compatability with the old -t option behaviour; only set by -t 0, cleared by everything else - char *output_name; - audio_output *output; - char *mdns_name; - mdns_backend *mdns; - int buffer_start_fill; - uint32_t latency; - uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution - uint32_t iTunesLatency; // supplied with --iTunesLatency option - uint32_t AirPlayLatency; //supplied with --AirPlayLatency option - uint32_t ForkedDaapdLatency; //supplied with --ForkedDaapdLatency option - int daemonise; - int statistics_requested; - char *cmd_start, *cmd_stop; - int cmd_blocking; - int tolerance; // allow this much drift before attempting to correct it - enum stuffing_type packet_stuffing; - char *pidfile; - char *logfile; - char *errfile; - char *configfile; - uint audio_backend_buffer_desired_length; // this will be the desired number of frames in the audio backend buffer -- the DAC buffer for ALSA - uint audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency there might be in the audio + uint8_t hw_addr[6]; + int port; + int ignore_volume_control; + int resyncthreshold; // if it get's out of whack my more than this, resync. Zero means never + // resync. + int allow_session_interruption; + int timeout; // while in play mode, exit if no packets of audio come in for more than this number + // of seconds . Zero means never exit. + int dont_check_timeout; // this is used to maintain backward compatability with the old -t option + // behaviour; only set by -t 0, cleared by everything else + char *output_name; + audio_output *output; + char *mdns_name; + mdns_backend *mdns; + int buffer_start_fill; + uint32_t latency; + uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution + uint32_t iTunesLatency; // supplied with --iTunesLatency option + uint32_t AirPlayLatency; // supplied with --AirPlayLatency option + uint32_t ForkedDaapdLatency; // supplied with --ForkedDaapdLatency option + int daemonise; + int statistics_requested; + char *cmd_start, *cmd_stop; + int cmd_blocking; + int tolerance; // allow this much drift before attempting to correct it + enum stuffing_type packet_stuffing; + char *pidfile; + char *logfile; + char *errfile; + char *configfile; + uint audio_backend_buffer_desired_length; // this will be the desired number of frames in the + // audio backend buffer -- the DAC buffer for ALSA + uint audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency + // there might be in the audio } shairport_cfg; -//true if Shairport Sync is supposed to be sending output to the output device, false otherwise +// true if Shairport Sync is supposed to be sending output to the output device, false otherwise int get_requested_connection_state_to_output(); @@ -96,10 +98,11 @@ uint8_t *base64_dec(char *input, int *outlen); char *base64_enc(uint8_t *input, int length); #define RSA_MODE_AUTH (0) -#define RSA_MODE_KEY (1) +#define RSA_MODE_KEY (1) uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode); -// given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60 dB), return an attenuation depending on the transfer function +// given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60 +// dB), return an attenuation depending on the transfer function double vol2attn(double vol, long max_db, long min_db); // return a monolithic (always increasing) time in nanoseconds @@ -24,7 +24,6 @@ * OTHER DEALINGS IN THE SOFTWARE. */ - #include <memory.h> #include <string.h> #include <stdio.h> @@ -47,78 +46,66 @@ extern mdns_backend mdns_tinysvcmdns; static mdns_backend *mdns_backends[] = { #ifdef CONFIG_AVAHI - &mdns_avahi, - &mdns_external_avahi, + &mdns_avahi, &mdns_external_avahi, #endif #ifdef CONFIG_HAVE_DNS_SD_H - &mdns_dns_sd, - &mdns_external_dns_sd, + &mdns_dns_sd, &mdns_external_dns_sd, #endif #ifdef CONFIG_TINYSVCMDNS &mdns_tinysvcmdns, #endif - NULL -}; + NULL}; void mdns_register(void) { - char *mdns_apname = alloca(strlen(config.apname) + 14); - char *p = mdns_apname; - int i; - for (i=0; i<6; i++) { - sprintf(p, "%02X", config.hw_addr[i]); - p += 2; - } - *p++ = '@'; - strcpy(p, config.apname); + char *mdns_apname = alloca(strlen(config.apname) + 14); + char *p = mdns_apname; + int i; + for (i = 0; i < 6; i++) { + sprintf(p, "%02X", config.hw_addr[i]); + p += 2; + } + *p++ = '@'; + strcpy(p, config.apname); - mdns_backend **b = NULL; - - if (config.mdns_name != NULL) - { - for (b = mdns_backends; *b; b++) - { - if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for - continue; - int error = (*b)->mdns_register(mdns_apname, config.port); - if (error >= 0) - { - config.mdns = *b; - } - break; - } + mdns_backend **b = NULL; - if (*b == NULL) - warn("%s mDNS backend not found"); + if (config.mdns_name != NULL) { + for (b = mdns_backends; *b; b++) { + if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for + continue; + int error = (*b)->mdns_register(mdns_apname, config.port); + if (error >= 0) { + config.mdns = *b; + } + break; } - else - { - for (b = mdns_backends; *b; b++) - { - int error = (*b)->mdns_register(mdns_apname, config.port); - if (error >= 0) - { - config.mdns = *b; - break; - } - } + + if (*b == NULL) + warn("%s mDNS backend not found"); + } else { + for (b = mdns_backends; *b; b++) { + int error = (*b)->mdns_register(mdns_apname, config.port); + if (error >= 0) { + config.mdns = *b; + break; + } } + } - if (config.mdns == NULL) - die("Could not establish mDNS advertisement!"); + if (config.mdns == NULL) + die("Could not establish mDNS advertisement!"); } void mdns_unregister(void) { - if (config.mdns) { - config.mdns->mdns_unregister(); - } + if (config.mdns) { + config.mdns->mdns_unregister(); + } } void mdns_ls_backends(void) { - mdns_backend **b = NULL; - printf("Available mDNS backends: \n"); - for (b = mdns_backends; *b; b++) - { - printf(" %s\n", (*b)->name); - } + mdns_backend **b = NULL; + printf("Available mDNS backends: \n"); + for (b = mdns_backends; *b; b++) { + printf(" %s\n", (*b)->name); + } } - @@ -10,9 +10,9 @@ void mdns_register(void); void mdns_ls_backends(void); typedef struct { - char *name; - int (*mdns_register)(char *apname, int port); - void (*mdns_unregister)(void); + char *name; + int (*mdns_register)(char *apname, int port); + void (*mdns_unregister)(void); } mdns_backend; // text and progress only -- picture feed really buggy from iTunes @@ -21,13 +21,13 @@ typedef struct { #define METADATA_EXPRESSION config.get_coverart ? "md=0,1,2" : "md=0,2" -#define MDNS_RECORD_WITH_METADATA "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION , \ - "ss=16", "sr=44100", "vn=3", "txtvers=1", \ - config.password ? "pw=true" : "pw=false" +#define MDNS_RECORD_WITH_METADATA \ + "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION, "ss=16", \ + "sr=44100", "vn=3", "txtvers=1", config.password ? "pw=true" : "pw=false" #endif -#define MDNS_RECORD_WITHOUT_METADATA "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", \ - "ss=16", "sr=44100", "vn=3", "txtvers=1", \ - config.password ? "pw=true" : "pw=false" +#define MDNS_RECORD_WITHOUT_METADATA \ + "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", "ss=16", "sr=44100", "vn=3", \ + "txtvers=1", config.password ? "pw=true" : "pw=false" #endif // _MDNS_H diff --git a/mdns_avahi.c b/mdns_avahi.c index 35e397d..31800b8 100644 --- a/mdns_avahi.c +++ b/mdns_avahi.c @@ -42,131 +42,104 @@ static AvahiThreadedPoll *tpoll = NULL; static char *name = NULL; static int port = 0; -static void egroup_callback(AvahiEntryGroup *g, - AvahiEntryGroupState state, +static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { - if (state==AVAHI_ENTRY_GROUP_COLLISION) - die("service name already exists on network!"); - if (state==AVAHI_ENTRY_GROUP_FAILURE) - die("avahi entry group failure!"); + if (state == AVAHI_ENTRY_GROUP_COLLISION) + die("service name already exists on network!"); + if (state == AVAHI_ENTRY_GROUP_FAILURE) + die("avahi entry group failure!"); } static void register_service(AvahiClient *c) { - debug(1, "avahi: register_service."); - if (!group) - group = avahi_entry_group_new(c, egroup_callback, NULL); - if (!group) - die("avahi_entry_group_new failed"); + debug(1, "avahi: register_service."); + if (!group) + group = avahi_entry_group_new(c, egroup_callback, NULL); + if (!group) + die("avahi_entry_group_new failed"); - if (!avahi_entry_group_is_empty(group)) - return; + if (!avahi_entry_group_is_empty(group)) + return; - int ret; + int ret; #ifdef CONFIG_METADATA - if (config.metadata_enabled) { - debug(1,"Avahi with metadata"); - ret = avahi_entry_group_add_service(group, - AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - 0, - name, - "_raop._tcp", - NULL, - NULL, - port, - MDNS_RECORD_WITH_METADATA, - NULL); - } - else { + if (config.metadata_enabled) { + debug(1, "Avahi with metadata"); + ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, + "_raop._tcp", NULL, NULL, port, MDNS_RECORD_WITH_METADATA, + NULL); + } else { #endif - debug(1,"Avahi without metadata"); - ret = avahi_entry_group_add_service(group, - AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - 0, - name, - "_raop._tcp", - NULL, - NULL, - port, - MDNS_RECORD_WITHOUT_METADATA, - NULL); -#ifdef CONFIG_METADATA - } + debug(1, "Avahi without metadata"); + ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, + "_raop._tcp", NULL, NULL, port, + MDNS_RECORD_WITHOUT_METADATA, NULL); +#ifdef CONFIG_METADATA + } #endif - - if (ret < 0) - die("avahi_entry_group_add_service failed"); - ret = avahi_entry_group_commit(group); - if (ret < 0) - die("avahi_entry_group_commit failed"); + if (ret < 0) + die("avahi_entry_group_add_service failed"); + + ret = avahi_entry_group_commit(group); + if (ret < 0) + die("avahi_entry_group_commit failed"); } -static void client_callback(AvahiClient *c, - AvahiClientState state, - AVAHI_GCC_UNUSED void * userdata) { - switch (state) { - case AVAHI_CLIENT_S_REGISTERING: - if (group) - avahi_entry_group_reset(group); - break; - - case AVAHI_CLIENT_S_RUNNING: - register_service(c); - break; - - case AVAHI_CLIENT_FAILURE: - case AVAHI_CLIENT_S_COLLISION: - die("avahi client failure"); - - case AVAHI_CLIENT_CONNECTING: - break; - } +static void client_callback(AvahiClient *c, AvahiClientState state, + AVAHI_GCC_UNUSED void *userdata) { + switch (state) { + case AVAHI_CLIENT_S_REGISTERING: + if (group) + avahi_entry_group_reset(group); + break; + + case AVAHI_CLIENT_S_RUNNING: + register_service(c); + break; + + case AVAHI_CLIENT_FAILURE: + case AVAHI_CLIENT_S_COLLISION: + die("avahi client failure"); + + case AVAHI_CLIENT_CONNECTING: + break; + } } static int avahi_register(char *srvname, int srvport) { - debug(1, "avahi: avahi_register."); - name = strdup(srvname); - port = srvport; - - int err; - if (!(tpoll = avahi_threaded_poll_new())) { - warn("couldn't create avahi threaded tpoll!"); - return -1; - } - if (!(client = avahi_client_new(avahi_threaded_poll_get(tpoll), - 0, - client_callback, - NULL, - &err))) { - warn("couldn't create avahi client: %s!", avahi_strerror(err)); - return -1; - } - - if (avahi_threaded_poll_start(tpoll) < 0) { - warn("couldn't start avahi tpoll thread"); - return -1; - } - - return 0; + debug(1, "avahi: avahi_register."); + name = strdup(srvname); + port = srvport; + + int err; + if (!(tpoll = avahi_threaded_poll_new())) { + warn("couldn't create avahi threaded tpoll!"); + return -1; + } + if (!(client = + avahi_client_new(avahi_threaded_poll_get(tpoll), 0, client_callback, NULL, &err))) { + warn("couldn't create avahi client: %s!", avahi_strerror(err)); + return -1; + } + + if (avahi_threaded_poll_start(tpoll) < 0) { + warn("couldn't start avahi tpoll thread"); + return -1; + } + + return 0; } static void avahi_unregister(void) { - debug(1, "avahi: avahi_unregister."); - if (tpoll) - avahi_threaded_poll_stop(tpoll); - tpoll = NULL; - - if (name) - free(name); - name = NULL; + debug(1, "avahi: avahi_unregister."); + if (tpoll) + avahi_threaded_poll_stop(tpoll); + tpoll = NULL; + + if (name) + free(name); + name = NULL; } -mdns_backend mdns_avahi = -{ - .name = "avahi", - .mdns_register = avahi_register, - .mdns_unregister = avahi_unregister -}; - +mdns_backend mdns_avahi = { + .name = "avahi", .mdns_register = avahi_register, .mdns_unregister = avahi_unregister}; diff --git a/mdns_dns_sd.c b/mdns_dns_sd.c index 4f586b9..922c2fb 100644 --- a/mdns_dns_sd.c +++ b/mdns_dns_sd.c @@ -32,79 +32,62 @@ static DNSServiceRef service; static int mdns_dns_sd_register(char *apname, int port) { - const char *recordwithoutmetadata[] = { MDNS_RECORD_WITHOUT_METADATA, NULL }; + const char *recordwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL}; #ifdef CONFIG_METADATA - const char *recordwithmetadata[] = { MDNS_RECORD_WITH_METADATA, NULL }; -#endif - char **record; + const char *recordwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL}; +#endif + char **record; #ifdef CONFIG_METADATA - if (config.meta_dir) - record = recordwithmetadata; - else + if (config.meta_dir) + record = recordwithmetadata; + else #endif - record = recordwithoutmetadata; - - uint16_t length = 0; - const char **field; + record = recordwithoutmetadata; + + uint16_t length = 0; + const char **field; - // Concatenate string contained i record into buf. + // Concatenate string contained i record into buf. - for (field = record; *field; field ++) - { - length += strlen(*field) + 1; // One byte for length each time - } + for (field = record; *field; field++) { + length += strlen(*field) + 1; // One byte for length each time + } - char *buf = malloc(length * sizeof(char)); - if (buf == NULL) - { - warn("dns_sd: buffer record allocation failed"); - return -1; - } + char *buf = malloc(length * sizeof(char)); + if (buf == NULL) { + warn("dns_sd: buffer record allocation failed"); + return -1; + } - char *p = buf; + char *p = buf; - for (field = record; *field; field ++) - { - char * newp = stpcpy(p + 1, *field); - *p = newp - p - 1; - p = newp; - } + for (field = record; *field; field++) { + char *newp = stpcpy(p + 1, *field); + *p = newp - p - 1; + p = newp; + } - DNSServiceErrorType error; - error = DNSServiceRegister(&service, - 0, - kDNSServiceInterfaceIndexAny, - apname, - "_raop._tcp", - "", - NULL, - htons((uint16_t)port), - length, - buf, - NULL, - NULL); + DNSServiceErrorType error; + error = DNSServiceRegister(&service, 0, kDNSServiceInterfaceIndexAny, apname, "_raop._tcp", "", + NULL, htons((uint16_t)port), length, buf, NULL, NULL); - free(buf); + free(buf); - if (error == kDNSServiceErr_NoError) - return 0; - else - { - warn("dns-sd: DNSServiceRegister error %d", error); - return -1; - } + if (error == kDNSServiceErr_NoError) + return 0; + else { + warn("dns-sd: DNSServiceRegister error %d", error); + return -1; + } } static void mdns_dns_sd_unregister(void) { - if (service) - { - DNSServiceRefDeallocate(service); - service = NULL; - } + if (service) { + DNSServiceRefDeallocate(service); + service = NULL; + } } -mdns_backend mdns_dns_sd = { - .name = "dns-sd", - .mdns_register = mdns_dns_sd_register, - .mdns_unregister = mdns_dns_sd_unregister -}; +mdns_backend mdns_dns_sd = {.name = "dns-sd", + .mdns_register = mdns_dns_sd_register, + .mdns_unregister = mdns_dns_sd_unregister}; diff --git a/mdns_external.c b/mdns_external.c index 48284e5..f9ba20c 100644 --- a/mdns_external.c +++ b/mdns_external.c @@ -41,145 +41,128 @@ int mdns_pid = 0; * Check errno for error details. */ static int fork_execvp(const char *file, char *const argv[]) { - int execpipe[2]; - int pid = 0; - if (pipe(execpipe) < 0) { - return -1; - } - - if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { - close(execpipe[0]); - close(execpipe[1]); - return -1; - } - - pid = fork(); - if (pid < 0) { - close(execpipe[0]); - close(execpipe[1]); - return -1; - } - else if(pid == 0) { // Child - close(execpipe[0]); // Close the read end - execvp(file, argv); + int execpipe[2]; + int pid = 0; + if (pipe(execpipe) < 0) { + return -1; + } - // If we reach this point then execve has failed. - // Write erno's value into the pipe and exit. - int ignore = write(execpipe[1], &errno, sizeof(errno)); + if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { + close(execpipe[0]); + close(execpipe[1]); + return -1; + } - _exit(-1); - return 0; // Just to make the compiler happy. - } - else { // Parent - close(execpipe[1]); // Close the write end - - int childErrno; - // Block until child closes the pipe or sends errno. - if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno)) { // We received errno - errno = childErrno; - return -1; - } - else { // Child closed the pipe. execvp was successful. - return pid; - } + pid = fork(); + if (pid < 0) { + close(execpipe[0]); + close(execpipe[1]); + return -1; + } else if (pid == 0) { // Child + close(execpipe[0]); // Close the read end + execvp(file, argv); + + // If we reach this point then execve has failed. + // Write erno's value into the pipe and exit. + int ignore = write(execpipe[1], &errno, sizeof(errno)); + + _exit(-1); + return 0; // Just to make the compiler happy. + } else { // Parent + close(execpipe[1]); // Close the write end + + int childErrno; + // Block until child closes the pipe or sends errno. + if (read(execpipe[0], &childErrno, sizeof(childErrno)) == + sizeof(childErrno)) { // We received errno + errno = childErrno; + return -1; + } else { // Child closed the pipe. execvp was successful. + return pid; } + } } static int mdns_external_avahi_register(char *apname, int port) { - char mdns_port[6]; - sprintf(mdns_port, "%d", config.port); + char mdns_port[6]; + sprintf(mdns_port, "%d", config.port); - char *argvwithoutmetadata[] = { - NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL - }; + char *argvwithoutmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, + MDNS_RECORD_WITHOUT_METADATA, NULL}; #ifdef CONFIG_METADATA - char *argvwithmetadata[] = { - NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, NULL - }; + char *argvwithmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, + NULL}; #endif - char **argv; + char **argv; #ifdef CONFIG_METADATA - if (config.metadata_enabled) - argv=argvwithmetadata; - else + if (config.metadata_enabled) + argv = argvwithmetadata; + else #endif - argv=argvwithoutmetadata; - - argv[0] = "avahi-publish-service"; - int pid = fork_execvp(argv[0], argv); - if (pid >= 0) - { - mdns_pid = pid; - return 0; - } - else - warn("Calling %s failed !", argv[0]); - - argv[0] = "mDNSPublish"; - pid = fork_execvp(argv[0], argv); - if (pid >= 0) - { - mdns_pid = pid; - return 0; - } - else - warn("Calling %s failed !", argv[0]); - - // If we reach here, both execvp calls failed. - return -1; + argv = argvwithoutmetadata; + + argv[0] = "avahi-publish-service"; + int pid = fork_execvp(argv[0], argv); + if (pid >= 0) { + mdns_pid = pid; + return 0; + } else + warn("Calling %s failed !", argv[0]); + + argv[0] = "mDNSPublish"; + pid = fork_execvp(argv[0], argv); + if (pid >= 0) { + mdns_pid = pid; + return 0; + } else + warn("Calling %s failed !", argv[0]); + + // If we reach here, both execvp calls failed. + return -1; } static int mdns_external_dns_sd_register(char *apname, int port) { - char mdns_port[6]; - sprintf(mdns_port, "%d", config.port); + char mdns_port[6]; + sprintf(mdns_port, "%d", config.port); - char *argvwithoutmetadata[] = { - NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL - }; + char *argvwithoutmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, + MDNS_RECORD_WITHOUT_METADATA, NULL}; #ifdef CONFIG_METADATA - char *argvwithmetadata[] = { - NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, NULL - }; + char *argvwithmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, + NULL}; #endif - char **argv; + char **argv; #ifdef CONFIG_METADATA - if (config.metadata_enabled) - argv=argvwithmetadata; - else + if (config.metadata_enabled) + argv = argvwithmetadata; + else #endif - argv=argvwithoutmetadata; + argv = argvwithoutmetadata; - int pid = fork_execvp(argv[0], argv); - if (pid >= 0) - { - mdns_pid = pid; - return 0; - } - else - warn("Calling %s failed !", argv[0]); + int pid = fork_execvp(argv[0], argv); + if (pid >= 0) { + mdns_pid = pid; + return 0; + } else + warn("Calling %s failed !", argv[0]); - return -1; + return -1; } static void kill_mdns_child(void) { - if (mdns_pid) - kill(mdns_pid, SIGTERM); - mdns_pid = 0; + if (mdns_pid) + kill(mdns_pid, SIGTERM); + mdns_pid = 0; } -mdns_backend mdns_external_avahi = { - .name = "external-avahi", - .mdns_register = mdns_external_avahi_register, - .mdns_unregister = kill_mdns_child -}; - -mdns_backend mdns_external_dns_sd = { - .name = "external-dns-sd", - .mdns_register = mdns_external_dns_sd_register, - .mdns_unregister = kill_mdns_child -}; +mdns_backend mdns_external_avahi = {.name = "external-avahi", + .mdns_register = mdns_external_avahi_register, + .mdns_unregister = kill_mdns_child}; +mdns_backend mdns_external_dns_sd = {.name = "external-dns-sd", + .mdns_register = mdns_external_dns_sd_register, + .mdns_unregister = kill_mdns_child}; diff --git a/mdns_tinysvcmdns.c b/mdns_tinysvcmdns.c index 8b79d49..a3c475a 100644 --- a/mdns_tinysvcmdns.c +++ b/mdns_tinysvcmdns.c @@ -27,7 +27,7 @@ #include <string.h> #include <sys/types.h> #include <sys/socket.h> -#include <net/if.h> +#include <net/if.h> #include <ifaddrs.h> #include <unistd.h> #include <netinet/in.h> @@ -39,128 +39,108 @@ static struct mdnsd *svr = NULL; static int mdns_tinysvcmdns_register(char *apname, int port) { - struct ifaddrs *ifalist; - struct ifaddrs *ifa; - - svr = mdnsd_start(); - if (svr == NULL) { - warn("tinysvcmdns: mdnsd_start() failed"); - return -1; - } - - // Thanks to Paul Lietar for this - // room for name + .local + NULL - char hostname[100 + 6]; - gethostname(hostname, 99); - // according to POSIX, this may be truncated without a final NULL ! - hostname[99] = 0; - - // will not work if the hostname doesn't end in .local - char *hostend = hostname + strlen(hostname); - if ((strlen(hostname) < strlen(".local")) || (strcmp(hostend - 6, ".local")!=0)) { - strcat(hostname, ".local"); - } - - if (getifaddrs(&ifalist) < 0) - { - warn("tinysvcmdns: getifaddrs() failed"); - return -1; + struct ifaddrs *ifalist; + struct ifaddrs *ifa; + + svr = mdnsd_start(); + if (svr == NULL) { + warn("tinysvcmdns: mdnsd_start() failed"); + return -1; + } + + // Thanks to Paul Lietar for this + // room for name + .local + NULL + char hostname[100 + 6]; + gethostname(hostname, 99); + // according to POSIX, this may be truncated without a final NULL ! + hostname[99] = 0; + + // will not work if the hostname doesn't end in .local + char *hostend = hostname + strlen(hostname); + if ((strlen(hostname) < strlen(".local")) || (strcmp(hostend - 6, ".local") != 0)) { + strcat(hostname, ".local"); + } + + if (getifaddrs(&ifalist) < 0) { + warn("tinysvcmdns: getifaddrs() failed"); + return -1; + } + + ifa = ifalist; + + // Look for an ipv4/ipv6 non-loopback interface to use as the main one. + for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next) { + if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { + uint32_t main_ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + + mdnsd_set_hostname(svr, hostname, main_ip); // TTL should be 120 seconds + break; + } else if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_INET6) { + struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + + mdnsd_set_hostname_v6(svr, hostname, addr); // TTL should be 120 seconds + break; } - - ifa = ifalist; - - // Look for an ipv4/ipv6 non-loopback interface to use as the main one. - for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next) - { - if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && - ifa->ifa_addr->sa_family == AF_INET) - { - uint32_t main_ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; - - mdnsd_set_hostname(svr, hostname, main_ip); // TTL should be 120 seconds - break; - } - else if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && - ifa->ifa_addr->sa_family == AF_INET6) - { - struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - - mdnsd_set_hostname_v6(svr, hostname, addr); // TTL should be 120 seconds - break; - } - } - - if (ifa == NULL) - { - warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found"); - return -1; + } + + if (ifa == NULL) { + warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found"); + return -1; + } + + // Skip the first one, it was already added by set_hostname + for (ifa = ifa->ifa_next; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_flags & IFF_LOOPBACK) // Skip loop-back interfaces + continue; + + switch (ifa->ifa_addr->sa_family) { + case AF_INET: { // ipv4 + uint32_t ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + struct rr_entry *a_e = rr_create_a(create_nlabel(hostname), ip); // TTL should be 120 seconds + mdnsd_add_rr(svr, a_e); + } break; + case AF_INET6: { // ipv6 + struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + struct rr_entry *aaaa_e = + rr_create_aaaa(create_nlabel(hostname), addr); // TTL should be 120 seconds + mdnsd_add_rr(svr, aaaa_e); + } break; } + } + freeifaddrs(ifa); - // Skip the first one, it was already added by set_hostname - for (ifa = ifa->ifa_next; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_flags & IFF_LOOPBACK) // Skip loop-back interfaces - continue; - - switch (ifa->ifa_addr->sa_family) - { - case AF_INET: { // ipv4 - uint32_t ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; - struct rr_entry *a_e = rr_create_a(create_nlabel(hostname), ip); // TTL should be 120 seconds - mdnsd_add_rr(svr, a_e); - } - break; - case AF_INET6: { // ipv6 - struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - struct rr_entry *aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // TTL should be 120 seconds - mdnsd_add_rr(svr, aaaa_e); - } - break; - } - } - - freeifaddrs(ifa); + char *txtwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL}; +#ifdef CONFIG_METADATA + char *txtwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL}; +#endif + char **txt; - char *txtwithoutmetadata[] = { MDNS_RECORD_WITHOUT_METADATA, NULL }; #ifdef CONFIG_METADATA - char *txtwithmetadata[] = { MDNS_RECORD_WITH_METADATA, NULL }; -#endif - char **txt; - -#ifdef CONFIG_METADATA - if (config.meta_dir) - txt = txtwithmetadata; - else + if (config.meta_dir) + txt = txtwithmetadata; + else #endif - txt = txtwithoutmetadata; + txt = txtwithoutmetadata; - - - struct mdns_service *svc = mdnsd_register_svc(svr, - apname, - "_raop._tcp.local", - port, - NULL, - (const char **)txt); // TTL should be 75 minutes, i.e. 4500 seconds + struct mdns_service *svc = + mdnsd_register_svc(svr, apname, "_raop._tcp.local", port, NULL, + (const char **)txt); // TTL should be 75 minutes, i.e. 4500 seconds - mdns_service_destroy(svc); + mdns_service_destroy(svc); - return 0; + return 0; } static void mdns_tinysvcmdns_unregister(void) { - if (svr) - { - mdnsd_stop(svr); - svr = NULL; - } + if (svr) { + mdnsd_stop(svr); + svr = NULL; + } } -mdns_backend mdns_tinysvcmdns = { - .name = "tinysvcmdns", - .mdns_register = mdns_tinysvcmdns_register, - .mdns_unregister = mdns_tinysvcmdns_unregister -}; - +mdns_backend mdns_tinysvcmdns = {.name = "tinysvcmdns", + .mdns_register = mdns_tinysvcmdns_register, + .mdns_unregister = mdns_tinysvcmdns_unregister}; @@ -72,18 +72,19 @@ static AES_KEY aes; #endif static int sampling_rate, frame_size; -#define FRAME_BYTES(frame_size) (4*frame_size) +#define FRAME_BYTES(frame_size) (4 * frame_size) // maximal resampling shift - conservative -#define OUTFRAME_BYTES(frame_size) (4*(frame_size+3)) +#define OUTFRAME_BYTES(frame_size) (4 * (frame_size + 3)) -#ifdef HAVE_LIBPOLARSSL +#ifdef HAVE_LIBPOLARSSL static aes_context dctx; #endif static pthread_t player_thread; static int please_stop; -static int connection_state_to_output; // if true, then play incoming stuff; if false drop everything +static int + connection_state_to_output; // if true, then play incoming stuff; if false drop everything static alac_file *decoder_info; @@ -99,13 +100,13 @@ static pthread_mutex_t vol_mutex = PTHREAD_MUTEX_INITIALIZER; // default buffer size // needs to be a power of 2 because of the way BUFIDX(seqno) works -#define BUFFER_FRAMES 512 -#define MAX_PACKET 2048 +#define BUFFER_FRAMES 512 +#define MAX_PACKET 2048 // DAC buffer occupancy stuff #define DAC_BUFFER_QUEUE_MINIMUM_LENGTH 5000 -typedef struct audio_buffer_entry { // decoded audio packets +typedef struct audio_buffer_entry { // decoded audio packets int ready; uint32_t timestamp; seq_t sequence_number; @@ -133,11 +134,11 @@ static int64_t first_packet_time_to_play; // nanoseconds static audio_parameters audio_information; // stats -static uint64_t missing_packets,late_packets,too_late_packets,resend_requests; +static uint64_t missing_packets, late_packets, too_late_packets, resend_requests; static void ab_resync(void) { int i; - for (i=0; i<BUFFER_FRAMES; i++) { + for (i = 0; i < BUFFER_FRAMES; i++) { audio_buffer[i].ready = 0; audio_buffer[i].sequence_number = 0; } @@ -148,19 +149,19 @@ static void ab_resync(void) { // the sequence number is a 16-bit unsigned number which wraps pretty often // to work out if one seqno is 'after' another therefore depends whether wrap has occurred -// this function works out the actual ordinate of the seqno, i.e. the distance up from +// this function works out the actual ordinate of the seqno, i.e. the distance up from // the zeroth element, at ab_read, taking due account of wrap. static inline seq_t SUCCESSOR(seq_t x) { uint32_t p = x & 0xffff; - p+=1; + p += 1; p = p & 0xffff; return p; } static inline seq_t PREDECESSOR(seq_t x) { - uint32_t p = (x & 0xffff)+0x10000; - p-=1; + uint32_t p = (x & 0xffff) + 0x10000; + p -= 1; p = p & 0xffff; return p; } @@ -169,7 +170,7 @@ static inline seq_t PREDECESSOR(seq_t x) { static inline int32_t ORDINATE(seq_t x) { int32_t p = x & 0xffff; int32_t q = ab_read & 0x0ffff; - int32_t t = (p+0x10000-q) & 0xffff; + int32_t t = (p + 0x10000 - q) & 0xffff; // we definitely will get a positive number in t at this point, but it might be a // positive alias of a negative number, i.e. x might actually be "before" ab_read // So, if the result is greater than 32767, we will assume its an @@ -183,63 +184,60 @@ static inline int32_t ORDINATE(seq_t x) { // wrapped number between two seq_t. int32_t seq_diff(seq_t a, seq_t b) { - int32_t diff = ORDINATE(b) - ORDINATE(a); - return diff; + int32_t diff = ORDINATE(b) - ORDINATE(a); + return diff; } // the sequence numbers will wrap pretty often. // this returns true if the second arg is after the first static inline int seq_order(seq_t a, seq_t b) { - int32_t d = ORDINATE(b) - ORDINATE(a); - return d > 0; + int32_t d = ORDINATE(b) - ORDINATE(a); + return d > 0; } static inline seq_t seq_sum(seq_t a, seq_t b) { uint32_t p = a & 0xffff; uint32_t q = b & 0x0ffff; - uint32_t r = (a+b) & 0xffff; + uint32_t r = (a + b) & 0xffff; return r; } - // now for 32-bit wrapping in timestamps // this returns true if the second arg is strictly after the first // on the assumption that the gap between them is never greater than (2^31)-1 // Represent a and b in 64 bits static inline int seq32_order(uint32_t a, uint32_t b) { - if (a==b) - return 0; - int64_t A = a & 0xffffffff; - int64_t B = b & 0xffffffff; - int64_t C = B-A; - // if bit 31 is set, it means either b is before (i.e. less than) a or - // b is (2^31)-1 ahead of a. - - // If we assume the gap between b and a should never reach 2 billion, then - // bit 31 == 0 means b is strictly after a + if (a == b) + return 0; + int64_t A = a & 0xffffffff; + int64_t B = b & 0xffffffff; + int64_t C = B - A; + // if bit 31 is set, it means either b is before (i.e. less than) a or + // b is (2^31)-1 ahead of a. + + // If we assume the gap between b and a should never reach 2 billion, then + // bit 31 == 0 means b is strictly after a return (C & 0x80000000) == 0; } - - static void alac_decode(short *dest, uint8_t *buf, int len) { unsigned char packet[MAX_PACKET]; unsigned char packetp[MAX_PACKET]; - assert(len<=MAX_PACKET); + assert(len <= MAX_PACKET); unsigned char iv[16]; int aeslen = len & ~0xf; memcpy(iv, aesiv, sizeof(iv)); - + #ifdef HAVE_LIBPOLARSSL - aes_crypt_cbc(&dctx,AES_DECRYPT,aeslen,iv,buf, packet); + aes_crypt_cbc(&dctx, AES_DECRYPT, aeslen, iv, buf, packet); #endif #ifdef HAVE_LIBSSL AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT); #endif - memcpy(packet+aeslen, buf+aeslen, len-aeslen); + memcpy(packet + aeslen, buf + aeslen, len - aeslen); int outsize; @@ -264,112 +262,115 @@ static int init_decoder(int32_t fmtp[12]) { decoder_info = alac; alac->setinfo_max_samples_per_frame = frame_size; - alac->setinfo_7a = fmtp[2]; + alac->setinfo_7a = fmtp[2]; alac->setinfo_sample_size = sample_size; alac->setinfo_rice_historymult = fmtp[4]; alac->setinfo_rice_initialhistory = fmtp[5]; alac->setinfo_rice_kmodifier = fmtp[6]; - alac->setinfo_7f = fmtp[7]; - alac->setinfo_80 = fmtp[8]; - alac->setinfo_82 = fmtp[9]; - alac->setinfo_86 = fmtp[10]; + alac->setinfo_7f = fmtp[7]; + alac->setinfo_80 = fmtp[8]; + alac->setinfo_82 = fmtp[9]; + alac->setinfo_86 = fmtp[10]; alac->setinfo_8a_rate = fmtp[11]; alac_allocate_buffers(alac); return 0; } -static void free_decoder(void) { - alac_free(decoder_info); -} +static void free_decoder(void) { alac_free(decoder_info); } static void init_buffer(void) { int i; - for (i=0; i<BUFFER_FRAMES; i++) + for (i = 0; i < BUFFER_FRAMES; i++) audio_buffer[i].data = malloc(OUTFRAME_BYTES(frame_size)); ab_resync(); } static void free_buffer(void) { int i; - for (i=0; i<BUFFER_FRAMES; i++) + for (i = 0; i < BUFFER_FRAMES; i++) free(audio_buffer[i].data); } -void player_put_packet(seq_t seqno,uint32_t timestamp, uint8_t *data, int len) { - +void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len) { + packet_count++; - + pthread_mutex_lock(&ab_mutex); - time_of_last_audio_packet = get_absolute_time_in_fp(); + time_of_last_audio_packet = get_absolute_time_in_fp(); if (connection_state_to_output) { // if we are supposed to be processing these packets - - if ((flush_rtp_timestamp!=0) && ((timestamp==flush_rtp_timestamp) || seq32_order(timestamp,flush_rtp_timestamp))) { - debug(2,"Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to timestamp: %u.",seqno,timestamp,flush_rtp_timestamp); - } else { - if ((flush_rtp_timestamp!=0x0) && (!seq32_order(timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time - flush_rtp_timestamp=0x0; - - abuf_t *abuf = 0; - - if (!ab_synced) { - debug(2, "syncing to seqno %u.", seqno); - ab_write = seqno; - ab_read = seqno; - ab_synced = 1; - } - if (ab_write == seqno) { // expected packet - abuf = audio_buffer + BUFIDX(seqno); - ab_write = SUCCESSOR(seqno); - } else if (seq_order(ab_write, seqno)) { // newer than expected - //if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8) - // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno: %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno); - int32_t gap = seq_diff(ab_write,PREDECESSOR(seqno))+1; - if (gap<=0) - debug(1,"Unexpected gap size: %d.",gap); - int i; - for (i=0;i<gap;i++) { - abuf = audio_buffer + BUFIDX(seq_sum(ab_write,i)); - abuf->ready = 0; // to be sure, to be sure - abuf->timestamp = 0; - abuf->sequence_number = 0; - } - // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write); - abuf = audio_buffer + BUFIDX(seqno); - rtp_request_resend(ab_write,gap); - resend_requests++; - ab_write = SUCCESSOR(seqno); - } else if (seq_order(ab_read, seqno)) { // late but not yet played - late_packets++; - abuf = audio_buffer + BUFIDX(seqno); - } else { // too late. - too_late_packets++; - /* - if (!late_packet_message_sent) { - debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read, ab_write); - late_packet_message_sent=1; - } - */ - } - // pthread_mutex_unlock(&ab_mutex); - - if (abuf) { - alac_decode(abuf->data, data, len); - abuf->ready = 1; - abuf->timestamp = timestamp; - abuf->sequence_number = seqno; - } - - // pthread_mutex_lock(&ab_mutex); - + + if ((flush_rtp_timestamp != 0) && + ((timestamp == flush_rtp_timestamp) || seq32_order(timestamp, flush_rtp_timestamp))) { + debug(2, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to " + "timestamp: %u.", + seqno, timestamp, flush_rtp_timestamp); + } else { + if ((flush_rtp_timestamp != 0x0) && + (!seq32_order(timestamp, + flush_rtp_timestamp))) // if we have gone past the flush boundary time + flush_rtp_timestamp = 0x0; + + abuf_t *abuf = 0; + + if (!ab_synced) { + debug(2, "syncing to seqno %u.", seqno); + ab_write = seqno; + ab_read = seqno; + ab_synced = 1; + } + if (ab_write == seqno) { // expected packet + abuf = audio_buffer + BUFIDX(seqno); + ab_write = SUCCESSOR(seqno); + } else if (seq_order(ab_write, seqno)) { // newer than expected + // if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8) + // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno: + // %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno); + int32_t gap = seq_diff(ab_write, PREDECESSOR(seqno)) + 1; + if (gap <= 0) + debug(1, "Unexpected gap size: %d.", gap); + int i; + for (i = 0; i < gap; i++) { + abuf = audio_buffer + BUFIDX(seq_sum(ab_write, i)); + abuf->ready = 0; // to be sure, to be sure + abuf->timestamp = 0; + abuf->sequence_number = 0; + } + // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write); + abuf = audio_buffer + BUFIDX(seqno); + rtp_request_resend(ab_write, gap); + resend_requests++; + ab_write = SUCCESSOR(seqno); + } else if (seq_order(ab_read, seqno)) { // late but not yet played + late_packets++; + abuf = audio_buffer + BUFIDX(seqno); + } else { // too late. + too_late_packets++; + /* + if (!late_packet_message_sent) { + debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read, + ab_write); + late_packet_message_sent=1; + } + */ + } + // pthread_mutex_unlock(&ab_mutex); + + if (abuf) { + alac_decode(abuf->data, data, len); + abuf->ready = 1; + abuf->timestamp = timestamp; + abuf->sequence_number = seqno; + } + + // pthread_mutex_lock(&ab_mutex); } int rc = pthread_cond_signal(&flowcontrol); if (rc) - debug(1,"Error signalling flowcontrol."); - } + debug(1, "Error signalling flowcontrol."); + } pthread_mutex_unlock(&ab_mutex); } - static inline short lcg_rand(void) { static unsigned long lcg_prev = 12345; lcg_prev = lcg_prev * 69069 + 3; @@ -387,7 +388,7 @@ static inline short dithered_vol(short sample) { out += rand_a; out -= rand_b; } - return out>>16; + return out >> 16; } // get the next frame, when available. return 0 if underrun/stream reset. @@ -398,132 +399,161 @@ static abuf_t *buffer_get_frame(void) { abuf_t *abuf = 0; int i; abuf_t *curframe; - + pthread_mutex_lock(&ab_mutex); int wait; int32_t dac_delay = 0; do { // get the time - local_time_now = get_absolute_time_in_fp(); - - // if config.timeout (default 120) seconds have elapsed since the last audio packet was received, then we should stop. - // config.timeout of zero means don't check..., but iTunes may be confused by a long gap followed by a resumption... - - if ((time_of_last_audio_packet!=0) && (shutdown_requested==0) && (config.dont_check_timeout==0)) { - uint64_t ct = config.timeout; // go from int to 64-bit int - if ((local_time_now>time_of_last_audio_packet) && (local_time_now-time_of_last_audio_packet>=ct<<32)) { - debug(1,"As Yeats almost said, \"Too long a silence / can make a stone of the heart\""); + local_time_now = get_absolute_time_in_fp(); + + // if config.timeout (default 120) seconds have elapsed since the last audio packet was + // received, then we should stop. + // config.timeout of zero means don't check..., but iTunes may be confused by a long gap + // followed by a resumption... + + if ((time_of_last_audio_packet != 0) && (shutdown_requested == 0) && + (config.dont_check_timeout == 0)) { + uint64_t ct = config.timeout; // go from int to 64-bit int + if ((local_time_now > time_of_last_audio_packet) && + (local_time_now - time_of_last_audio_packet >= ct << 32)) { + debug(1, "As Yeats almost said, \"Too long a silence / can make a stone of the heart\""); rtsp_request_shutdown_stream(); - shutdown_requested=1; + shutdown_requested = 1; } } int rco = get_requested_connection_state_to_output(); - + if (connection_state_to_output != rco) { - connection_state_to_output=rco; + connection_state_to_output = rco; // change happening - if (connection_state_to_output==0) { //going off - pthread_mutex_lock(&flush_mutex); - flush_requested=1; - pthread_mutex_unlock(&flush_mutex); + if (connection_state_to_output == 0) { // going off + pthread_mutex_lock(&flush_mutex); + flush_requested = 1; + pthread_mutex_unlock(&flush_mutex); } } - + pthread_mutex_lock(&flush_mutex); - if (flush_requested==1) { + if (flush_requested == 1) { if (config.output->flush) config.output->flush(); ab_resync(); first_packet_timestamp = 0; first_packet_time_to_play = 0; - flush_requested=0; + flush_requested = 0; } pthread_mutex_unlock(&flush_mutex); uint32_t flush_limit = 0; if (ab_synced) { - do { - curframe = audio_buffer + BUFIDX(ab_read); - if (curframe->ready) { - - if (curframe->sequence_number!=ab_read) { - // some kind of sync problem has occurred. - if (BUFIDX(curframe->sequence_number)==BUFIDX(ab_read)) { - // it looks like some kind of aliasing has happened - if (seq_order(ab_read,curframe->sequence_number)) { - ab_read=curframe->sequence_number; - debug(1,"Aliasing of buffer index -- reset."); - } - } else { - debug(1,"Inconsistent sequence numbers detected"); - } - } - - if ((flush_rtp_timestamp!=0) && ((curframe->timestamp==flush_rtp_timestamp) || seq32_order(curframe->timestamp,flush_rtp_timestamp))) { - debug(1,"Dropping flushed packet seqno %u, timestamp %u",curframe->sequence_number,curframe->timestamp); - curframe->ready=0; - flush_limit++; - ab_read=SUCCESSOR(ab_read); - } - if ((flush_rtp_timestamp!=0) && (!seq32_order(curframe->timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time - flush_rtp_timestamp=0; - } - } while ((flush_rtp_timestamp!=0) && (flush_limit<=8820) && (curframe->ready==0)); - - if (flush_limit==8820) { - debug(1,"Flush hit the 8820 frame limit!"); - flush_limit=0; - } - + do { + curframe = audio_buffer + BUFIDX(ab_read); + if (curframe->ready) { + + if (curframe->sequence_number != ab_read) { + // some kind of sync problem has occurred. + if (BUFIDX(curframe->sequence_number) == BUFIDX(ab_read)) { + // it looks like some kind of aliasing has happened + if (seq_order(ab_read, curframe->sequence_number)) { + ab_read = curframe->sequence_number; + debug(1, "Aliasing of buffer index -- reset."); + } + } else { + debug(1, "Inconsistent sequence numbers detected"); + } + } + + if ((flush_rtp_timestamp != 0) && + ((curframe->timestamp == flush_rtp_timestamp) || + seq32_order(curframe->timestamp, flush_rtp_timestamp))) { + debug(1, "Dropping flushed packet seqno %u, timestamp %u", curframe->sequence_number, + curframe->timestamp); + curframe->ready = 0; + flush_limit++; + ab_read = SUCCESSOR(ab_read); + } + if ((flush_rtp_timestamp != 0) && + (!seq32_order(curframe->timestamp, + flush_rtp_timestamp))) // if we have gone past the flush boundary time + flush_rtp_timestamp = 0; + } + } while ((flush_rtp_timestamp != 0) && (flush_limit <= 8820) && (curframe->ready == 0)); + + if (flush_limit == 8820) { + debug(1, "Flush hit the 8820 frame limit!"); + flush_limit = 0; + } + curframe = audio_buffer + BUFIDX(ab_read); if (curframe->ready) { - if (ab_buffering) { // if we are getting packets but not yet forwarding them to the player - if (first_packet_timestamp==0) { // if this is the very first packet - // debug(1,"First frame seen, time %u, with %d frames...",curframe->timestamp,seq_diff(ab_read, ab_write)); - uint32_t reference_timestamp; + if (ab_buffering) { // if we are getting packets but not yet forwarding them to the player + if (first_packet_timestamp == 0) { // if this is the very first packet + // debug(1,"First frame seen, time %u, with %d + // frames...",curframe->timestamp,seq_diff(ab_read, ab_write)); + uint32_t reference_timestamp; uint64_t reference_timestamp_time; - get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time); + get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time); if (reference_timestamp) { // if we have a reference time // debug(1,"First frame seen with timestamp..."); - first_packet_timestamp=curframe->timestamp; // we will keep buffering until we are supposed to start playing this - - // Here, calculate when we should start playing. We need to know when to allow the packets to be sent to the player. - // We will send packets of silence from now until that time and then we will send the first packet, + first_packet_timestamp = curframe->timestamp; // we will keep buffering until we are + // supposed to start playing this + + // Here, calculate when we should start playing. We need to know when to allow the + // packets to be sent to the player. + // We will send packets of silence from now until that time and then we will send the + // first packet, // which will be followed by the subsequent packets. - + // we will get a fix every second or so, which will be stored as a pair consisting of - // the time when the packet with a particular timestamp should be played, neglecting latencies, etc. - - // It probably won't be the timestamp of our first packet, however, so we might have to do some calculations. - - // To calculate when the first packet will be played, we figure out the exact time the packet should - // be played according to its timestamp and the reference time. We then need to add the desired latency, typically 88200 frames. - - // Then we need to offset this by the backend latency offset. For example, if we knew that the audio back end has a latency of 100 ms, we would - // ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410 frames, so that when it got through the audio back end, - // if would be in sync. To do this, we would give it a latency offset of -100 ms, i.e. -4410 frames. - - int64_t delta = ((int64_t)first_packet_timestamp-(int64_t)reference_timestamp); - - first_packet_time_to_play = reference_timestamp_time+((delta+(int64_t)config.latency+(int64_t)config.audio_backend_latency_offset)<<32)/44100; - - if (local_time_now>=first_packet_time_to_play) { - debug(1,"First packet is late! It should have played before now. Flushing 0.1 seconds"); - player_flush(first_packet_timestamp+4410); + // the time when the packet with a particular timestamp should be played, neglecting + // latencies, etc. + + // It probably won't be the timestamp of our first packet, however, so we might have + // to do some calculations. + + // To calculate when the first packet will be played, we figure out the exact time the + // packet should + // be played according to its timestamp and the reference time. We then need to add + // the desired latency, typically 88200 frames. + + // Then we need to offset this by the backend latency offset. For example, if we knew + // that the audio back end has a latency of 100 ms, we would + // ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410 + // frames, so that when it got through the audio back end, + // if would be in sync. To do this, we would give it a latency offset of -100 ms, i.e. + // -4410 frames. + + int64_t delta = ((int64_t)first_packet_timestamp - (int64_t)reference_timestamp); + + first_packet_time_to_play = + reference_timestamp_time + + ((delta + (int64_t)config.latency + (int64_t)config.audio_backend_latency_offset) + << 32) / + 44100; + + if (local_time_now >= first_packet_time_to_play) { + debug( + 1, + "First packet is late! It should have played before now. Flushing 0.1 seconds"); + player_flush(first_packet_timestamp + 4410); } } - } + } - if (first_packet_time_to_play!=0) { + if (first_packet_time_to_play != 0) { uint32_t filler_size = frame_size; uint32_t max_dac_delay = 4410; filler_size = 4410; // 0.1 second -- the maximum we'll add to the DAC - if (local_time_now>=first_packet_time_to_play) { + if (local_time_now >= first_packet_time_to_play) { // we've gone past the time... - // debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp of %llx and dac_delay of %d and %d packets; flush.",(((tn-first_packet_time_to_play)*44100)>>32)+dac_delay,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, ab_write)); - + // debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp + // of %llx and dac_delay of %d and %d packets; + // flush.",(((tn-first_packet_time_to_play)*44100)>>32)+dac_delay,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, + // ab_write)); + if (config.output->flush) config.output->flush(); ab_resync(); @@ -532,40 +562,49 @@ static abuf_t *buffer_get_frame(void) { } else { if (config.output->delay) { dac_delay = config.output->delay(); - if (dac_delay==-1) { - debug(1,"Error getting dac_delay in buffer_get_frame."); - dac_delay=0; + if (dac_delay == -1) { + debug(1, "Error getting dac_delay in buffer_get_frame."); + dac_delay = 0; } } else - dac_delay=0; - uint64_t gross_frame_gap = ((first_packet_time_to_play-local_time_now)*44100)>>32; - int64_t exact_frame_gap = gross_frame_gap-dac_delay; - if (exact_frame_gap<=0) { + dac_delay = 0; + uint64_t gross_frame_gap = + ((first_packet_time_to_play - local_time_now) * 44100) >> 32; + int64_t exact_frame_gap = gross_frame_gap - dac_delay; + if (exact_frame_gap <= 0) { // we've gone past the time... - // debug(1,"Run a bit past the exact start time by %lld frames, with time now of %llx, fpttp of %llx and dac_delay of %d and %d packets; flush.",-exact_frame_gap,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, ab_write)); + // debug(1,"Run a bit past the exact start time by %lld frames, with time now of + // %llx, fpttp of %llx and dac_delay of %d and %d packets; + // flush.",-exact_frame_gap,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, + // ab_write)); if (config.output->flush) config.output->flush(); ab_resync(); first_packet_timestamp = 0; first_packet_time_to_play = 0; } else { - uint32_t fs=filler_size; - if (fs>(max_dac_delay-dac_delay)) - fs=max_dac_delay-dac_delay; - if ((exact_frame_gap<=fs) || (exact_frame_gap<=frame_size*2)) { - fs=exact_frame_gap; - // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, with %d packets, ab_read is %04x, ab_write is %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write),ab_read,ab_write); + uint32_t fs = filler_size; + if (fs > (max_dac_delay - dac_delay)) + fs = max_dac_delay - dac_delay; + if ((exact_frame_gap <= fs) || (exact_frame_gap <= frame_size * 2)) { + fs = exact_frame_gap; + // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, + // with %d packets, ab_read is %04x, ab_write is + // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, + // ab_write),ab_read,ab_write); ab_buffering = 0; } signed short *silence; silence = malloc(FRAME_BYTES(fs)); memset(silence, 0, FRAME_BYTES(fs)); - // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, with %d packets.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write)); + // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, + // with %d packets.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write)); config.output->play(silence, fs); free(silence); #ifdef CONFIG_METADATA - if (ab_buffering==0) { - send_ssnc_metadata('prsm',NULL,0,0); // "resume", but don't wait if the queue is locked + if (ab_buffering == 0) { + send_ssnc_metadata('prsm', NULL, 0, + 0); // "resume", but don't wait if the queue is locked } #endif } @@ -574,74 +613,79 @@ static abuf_t *buffer_get_frame(void) { } } } - + // Here, we work out whether to release a packet or wait // We release a buffer when the time is right. - - // To work out when the time is right, we need to take account of (1) the actual time the packet should be released, - // (2) the latency requested, (3) the audio backend latency offset and (4) the desired length of the audio backend's buffer - + + // To work out when the time is right, we need to take account of (1) the actual time the packet + // should be released, + // (2) the latency requested, (3) the audio backend latency offset and (4) the desired length of + // the audio backend's buffer + // The time is right if the current time is later or the same as // The packet time + (latency + latency offset - backend_buffer_length). // Note: the last three items are expressed in frames and must be converted to time. - int do_wait = 1; - if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) { - uint32_t reference_timestamp; - uint64_t reference_timestamp_time; - get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time); - if (reference_timestamp) { // if we have a reference time - uint32_t packet_timestamp=curframe->timestamp; - int64_t delta = ((int64_t)packet_timestamp-(int64_t)reference_timestamp); - int64_t offset = (int64_t)config.latency+config.audio_backend_latency_offset-(int64_t)config.audio_backend_buffer_desired_length; - int64_t net_offset = delta+offset; - int64_t time_to_play = reference_timestamp_time; - int64_t net_offset_fp_sec; - if (net_offset>=0) { - net_offset_fp_sec = (net_offset<<32)/44100; - time_to_play+=net_offset_fp_sec; // using the latency requested... - // debug(2,"Net Offset: %lld, adjusted: %lld.",net_offset,net_offset_fp_sec); - } else { - net_offset_fp_sec = ((-net_offset)<<32)/44100; - time_to_play-=net_offset_fp_sec; - // debug(2,"Net Offset: %lld, adjusted: -%lld.",net_offset,net_offset_fp_sec); - } - - if (local_time_now>=time_to_play) { - do_wait = 0; - } - } - } - wait = (ab_buffering || (do_wait!=0) || (!ab_synced)) && (!please_stop); + int do_wait = 1; + if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) { + uint32_t reference_timestamp; + uint64_t reference_timestamp_time; + get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time); + if (reference_timestamp) { // if we have a reference time + uint32_t packet_timestamp = curframe->timestamp; + int64_t delta = ((int64_t)packet_timestamp - (int64_t)reference_timestamp); + int64_t offset = (int64_t)config.latency + config.audio_backend_latency_offset - + (int64_t)config.audio_backend_buffer_desired_length; + int64_t net_offset = delta + offset; + int64_t time_to_play = reference_timestamp_time; + int64_t net_offset_fp_sec; + if (net_offset >= 0) { + net_offset_fp_sec = (net_offset << 32) / 44100; + time_to_play += net_offset_fp_sec; // using the latency requested... + // debug(2,"Net Offset: %lld, adjusted: %lld.",net_offset,net_offset_fp_sec); + } else { + net_offset_fp_sec = ((-net_offset) << 32) / 44100; + time_to_play -= net_offset_fp_sec; + // debug(2,"Net Offset: %lld, adjusted: -%lld.",net_offset,net_offset_fp_sec); + } + + if (local_time_now >= time_to_play) { + do_wait = 0; + } + } + } + wait = (ab_buffering || (do_wait != 0) || (!ab_synced)) && (!please_stop); if (wait) { - uint64_t time_to_wait_for_wakeup_fp = ((uint64_t)1<<32)/44100; // this is time period of one frame - time_to_wait_for_wakeup_fp *= 4*352; // four full 352-frame packets - time_to_wait_for_wakeup_fp /= 3; //four thirds of a packet time - + uint64_t time_to_wait_for_wakeup_fp = + ((uint64_t)1 << 32) / 44100; // this is time period of one frame + time_to_wait_for_wakeup_fp *= 4 * 352; // four full 352-frame packets + time_to_wait_for_wakeup_fp /= 3; // four thirds of a packet time + #ifdef COMPILE_FOR_LINUX - uint64_t time_of_wakeup_fp = local_time_now+time_to_wait_for_wakeup_fp; - uint64_t sec = time_of_wakeup_fp>>32; - uint64_t nsec = ((time_of_wakeup_fp&0xffffffff)*1000000000)>>32; - + uint64_t time_of_wakeup_fp = local_time_now + time_to_wait_for_wakeup_fp; + uint64_t sec = time_of_wakeup_fp >> 32; + uint64_t nsec = ((time_of_wakeup_fp & 0xffffffff) * 1000000000) >> 32; + struct timespec time_of_wakeup; time_of_wakeup.tv_sec = sec; time_of_wakeup.tv_nsec = nsec; - - pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup); - // int rc = pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup); - // if (rc!=0) - // debug(1,"pthread_cond_timedwait returned error code %d.",rc); + + pthread_cond_timedwait(&flowcontrol, &ab_mutex, &time_of_wakeup); +// int rc = pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup); +// if (rc!=0) +// debug(1,"pthread_cond_timedwait returned error code %d.",rc); #endif #ifdef COMPILE_FOR_OSX - uint64_t sec = time_to_wait_for_wakeup_fp>>32;; - uint64_t nsec = ((time_to_wait_for_wakeup_fp&0xffffffff)*1000000000)>>32; + uint64_t sec = time_to_wait_for_wakeup_fp >> 32; + ; + uint64_t nsec = ((time_to_wait_for_wakeup_fp & 0xffffffff) * 1000000000) >> 32; struct timespec time_to_wait; time_to_wait.tv_sec = sec; time_to_wait.tv_nsec = nsec; - pthread_cond_timedwait_relative_np(&flowcontrol,&ab_mutex,&time_to_wait); + pthread_cond_timedwait_relative_np(&flowcontrol, &ab_mutex, &time_to_wait); #endif - } + } } while (wait); if (please_stop) { @@ -653,11 +697,10 @@ static abuf_t *buffer_get_frame(void) { // check if t+8, t+16, t+32, t+64, t+128, ... (buffer_start_fill / 2) // packets have arrived... last-chance resend - - + if (!ab_buffering) { - for (i = 8; i < (seq_diff(ab_read,ab_write) / 2); i = (i * 2)) { - seq_t next = seq_sum(ab_read,i); + for (i = 8; i < (seq_diff(ab_read, ab_write) / 2); i = (i * 2)) { + seq_t next = seq_sum(ab_read, i); abuf = audio_buffer + BUFIDX(next); if (!abuf->ready) { rtp_request_resend(next, 1); @@ -666,15 +709,15 @@ static abuf_t *buffer_get_frame(void) { } } } - + if (!curframe->ready) { // debug(1, " %d. Supplying a silent frame.", read); missing_packets++; memset(curframe->data, 0, FRAME_BYTES(frame_size)); - curframe->timestamp=0; + curframe->timestamp = 0; } curframe->ready = 0; - ab_read=SUCCESSOR(ab_read); + ab_read = SUCCESSOR(ab_read); pthread_mutex_unlock(&ab_mutex); return curframe; } @@ -682,65 +725,66 @@ static abuf_t *buffer_get_frame(void) { static inline short shortmean(short a, short b) { long al = (long)a; long bl = (long)b; - long longmean = (al+bl)/2; + long longmean = (al + bl) / 2; short r = (short)longmean; - if (r!=longmean) - debug(1,"Error calculating average of two shorts"); + if (r != longmean) + debug(1, "Error calculating average of two shorts"); return r; } // stuff: 1 means add 1; 0 means do nothing; -1 means remove 1 static int stuff_buffer_basic(short *inptr, short *outptr, int stuff) { - if ((stuff>1) || (stuff<-1)) { - debug(1,"Stuff argument to stuff_buffer must be from -1 to +1."); - return frame_size; + if ((stuff > 1) || (stuff < -1)) { + debug(1, "Stuff argument to stuff_buffer must be from -1 to +1."); + return frame_size; + } + int i; + int stuffsamp = frame_size; + if (stuff) + // stuffsamp = rand() % (frame_size - 1); + stuffsamp = + (rand() % (frame_size - 2)) + 1; // ensure there's always a sample before and after the item + + pthread_mutex_lock(&vol_mutex); + for (i = 0; i < stuffsamp; i++) { // the whole frame, if no stuffing + *outptr++ = dithered_vol(*inptr++); + *outptr++ = dithered_vol(*inptr++); + }; + if (stuff) { + if (stuff == 1) { + debug(3, "+++++++++"); + // interpolate one sample + //*outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1); + //*outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1); + *outptr++ = dithered_vol(shortmean(inptr[-2], inptr[0])); + *outptr++ = dithered_vol(shortmean(inptr[-1], inptr[1])); + } else if (stuff == -1) { + debug(3, "---------"); + inptr++; + inptr++; } - int i; - int stuffsamp = frame_size; - if (stuff) -// stuffsamp = rand() % (frame_size - 1); - stuffsamp = (rand() % (frame_size-2))+1; // ensure there's always a sample before and after the item - - pthread_mutex_lock(&vol_mutex); - for (i=0; i<stuffsamp; i++) { // the whole frame, if no stuffing - *outptr++ = dithered_vol(*inptr++); - *outptr++ = dithered_vol(*inptr++); - }; - if (stuff) { - if (stuff==1) { - debug(3, "+++++++++"); - // interpolate one sample - //*outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1); - //*outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1); - *outptr++ = dithered_vol(shortmean(inptr[-2],inptr[0])); - *outptr++ = dithered_vol(shortmean(inptr[-1],inptr[1])); - } else if (stuff==-1) { - debug(3, "---------"); - inptr++; - inptr++; - } - for (i=stuffsamp; i<frame_size + stuff; i++) { - *outptr++ = dithered_vol(*inptr++); - *outptr++ = dithered_vol(*inptr++); - } + for (i = stuffsamp; i < frame_size + stuff; i++) { + *outptr++ = dithered_vol(*inptr++); + *outptr++ = dithered_vol(*inptr++); } - pthread_mutex_unlock(&vol_mutex); + } + pthread_mutex_unlock(&vol_mutex); - return frame_size + stuff; + return frame_size + stuff; } #ifdef HAVE_LIBSOXR // stuff: 1 means add 1; 0 means do nothing; -1 means remove 1 static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { - if ((stuff>1) || (stuff<-1)) { - debug(1,"Stuff argument to sox_stuff_buffer must be from -1 to +1."); + if ((stuff > 1) || (stuff < -1)) { + debug(1, "Stuff argument to sox_stuff_buffer must be from -1 to +1."); return frame_size; } int i; - short *ip,*op; - ip=inptr; - op=outptr; - + short *ip, *op; + ip = inptr; + op = outptr; + if (stuff) { // debug(1,"Stuff %d.",stuff); soxr_io_spec_t io_spec; @@ -753,10 +797,10 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { size_t odone; soxr_error_t error = soxr_oneshot(frame_size, frame_size + stuff, 2, /* Rates and # of chans. */ - inptr, frame_size, NULL, /* Input. */ - outptr, frame_size + stuff, &odone, /* Output. */ - &io_spec, /* Input, output and transfer spec. */ - NULL, NULL); /* Default configuration.*/ + inptr, frame_size, NULL, /* Input. */ + outptr, frame_size + stuff, &odone, /* Output. */ + &io_spec, /* Input, output and transfer spec. */ + NULL, NULL); /* Default configuration.*/ if (error) die("soxr error: %s\n", "error: %s\n", soxr_strerror(error)); @@ -765,26 +809,26 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { die("odone = %d!\n", odone); const int gpm = 5; - + // keep the first (dpm) samples, to mitigate the Gibbs phenomenon - for (i=0;i<gpm;i++) { + for (i = 0; i < gpm; i++) { *op++ = *ip++; *op++ = *ip++; } // keep the last (dpm) samples, to mitigate the Gibbs phenomenon - op=outptr+(frame_size+stuff-gpm)*sizeof(short); - ip=inptr+(frame_size-gpm)*sizeof(short); - for (i=0;i<gpm;i++) { + op = outptr + (frame_size + stuff - gpm) * sizeof(short); + ip = inptr + (frame_size - gpm) * sizeof(short); + for (i = 0; i < gpm; i++) { *op++ = *ip++; *op++ = *ip++; } // finally, adjust the volume, if necessary - if (software_mixer_volume!=1.0) { + if (software_mixer_volume != 1.0) { // pthread_mutex_lock(&vol_mutex); - op=outptr; - for (i=0; i<frame_size+stuff; i++) { + op = outptr; + for (i = 0; i < frame_size + stuff; i++) { *op = dithered_vol(*op); op++; *op = dithered_vol(*op); @@ -792,11 +836,11 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { }; // pthread_mutex_unlock(&vol_mutex); } - + } else { // the whole frame, if no stuffing - + // pthread_mutex_lock(&vol_mutex); - for (i=0; i<frame_size; i++) { + for (i = 0; i < frame_size; i++) { *op++ = dithered_vol(*ip++); *op++ = dithered_vol(*ip++); }; @@ -807,24 +851,25 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { #endif typedef struct stats { // statistics for running averages - int64_t sync_error,correction,drift; + int64_t sync_error, correction, drift; } stats_t; static void *player_thread_func(void *arg) { connection_state_to_output = get_requested_connection_state_to_output(); -//this is about half a minute +// this is about half a minute #define trend_interval 3758 stats_t statistics[trend_interval]; - int number_of_statistics,oldest_statistic,newest_statistic; + int number_of_statistics, oldest_statistic, newest_statistic; int at_least_one_frame_seen = 0; - int64_t tsum_of_sync_errors,tsum_of_corrections,tsum_of_insertions_and_deletions,tsum_of_drifts; - int64_t previous_sync_error,previous_correction; + int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions, + tsum_of_drifts; + int64_t previous_sync_error, previous_correction; int64_t minimum_dac_queue_size = 1000000; int32_t minimum_buffer_occupancy = BUFFER_FRAMES; int32_t maximum_buffer_occupancy = 0; - - audio_information.valid=0; - + + audio_information.valid = 0; + int play_samples; int64_t current_delay; int play_number = 0; @@ -834,19 +879,22 @@ static void *player_thread_func(void *arg) { tsum_of_sync_errors = tsum_of_corrections = tsum_of_insertions_and_deletions = tsum_of_drifts = 0; const int print_interval = trend_interval; // don't ask... - // I think it's useful to keep this prime to prevent it from falling into a pattern with some other process. - - char rnstate[256]; - initstate(time(NULL),rnstate,256); - + // I think it's useful to keep this prime to prevent it from falling into a pattern with some + // other process. + + char rnstate[256]; + initstate(time(NULL), rnstate, 256); + signed short *inbuf, *outbuf, *silence; outbuf = malloc(OUTFRAME_BYTES(frame_size)); silence = malloc(OUTFRAME_BYTES(frame_size)); memset(silence, 0, OUTFRAME_BYTES(frame_size)); - late_packet_message_sent=0; - missing_packets=late_packets=too_late_packets=resend_requests=0; - flush_rtp_timestamp=0; // it seems this number has a special significance -- it seems to be used as a null operand, so we'll use it like that too - int sync_error_out_of_bounds = 0; // number of times in a row that there's been a serious sync error + late_packet_message_sent = 0; + missing_packets = late_packets = too_late_packets = resend_requests = 0; + flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to be used + // as a null operand, so we'll use it like that too + int sync_error_out_of_bounds = + 0; // number of times in a row that there's been a serious sync error while (!please_stop) { abuf_t *inframe = buffer_get_frame(); if (inframe) { @@ -854,223 +902,246 @@ static void *player_thread_func(void *arg) { if (inbuf) { play_number++; // if it's a supplied silent frame, let us know... - if (inframe->timestamp==0) { + if (inframe->timestamp == 0) { // debug(1,"Player has a supplied silent frame."); - last_seqno_read = (SUCCESSOR(last_seqno_read)&0xffff); //manage the packet out of sequence minder + last_seqno_read = + (SUCCESSOR(last_seqno_read) & 0xffff); // manage the packet out of sequence minder config.output->play(inbuf, frame_size); } else { - // We have a frame of data. We need to see if we want to add or remove a frame from it to keep in sync. + // We have a frame of data. We need to see if we want to add or remove a frame from it to + // keep in sync. // So we calculate the timing error for the first frame in the DAC. // If it's ahead of time, we add one audio frame to this frame to delay a subsequent frame - // If it's late, we remove an audio frame from this frame to bring a subsequent frame forward in time - - at_least_one_frame_seen = 1; - - uint32_t reference_timestamp; + // If it's late, we remove an audio frame from this frame to bring a subsequent frame + // forward in time + + at_least_one_frame_seen = 1; + + uint32_t reference_timestamp; uint64_t reference_timestamp_time; - get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time); + get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time); - int64_t rt,nt; + int64_t rt, nt; rt = reference_timestamp; nt = inframe->timestamp; - + uint64_t local_time_now = get_absolute_time_in_fp(); - //struct timespec tn; - //clock_gettime(CLOCK_MONOTONIC,&tn); - //uint64_t local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000; + // struct timespec tn; + // clock_gettime(CLOCK_MONOTONIC,&tn); + // uint64_t + // local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000; int64_t td_in_frames; - int64_t td = local_time_now-reference_timestamp_time; - // debug(1,"td is %lld.",td); - if (td>=0) { - td_in_frames = (td*44100)>>32; + int64_t td = local_time_now - reference_timestamp_time; + // debug(1,"td is %lld.",td); + if (td >= 0) { + td_in_frames = (td * 44100) >> 32; } else { - td_in_frames = -((-td*44100)>>32); + td_in_frames = -((-td * 44100) >> 32); } - + // This is the timing error for the next audio frame in the DAC, if applicable - int64_t sync_error =0; - + int64_t sync_error = 0; + int amount_to_stuff = 0; - + // check sequencing - if (last_seqno_read==-1) - last_seqno_read=inframe->sequence_number; + if (last_seqno_read == -1) + last_seqno_read = inframe->sequence_number; else { - last_seqno_read = (SUCCESSOR(last_seqno_read) & 0xffff); - if (inframe->sequence_number!=last_seqno_read) { - debug(1,"Player: packets out of sequence: expected: %d, got: %d, sync error: %d frames.",last_seqno_read,inframe->sequence_number,sync_error); - last_seqno_read=inframe->sequence_number; // reset warning... + last_seqno_read = (SUCCESSOR(last_seqno_read) & 0xffff); + if (inframe->sequence_number != last_seqno_read) { + debug( + 1, + "Player: packets out of sequence: expected: %d, got: %d, sync error: %d frames.", + last_seqno_read, inframe->sequence_number, sync_error); + last_seqno_read = inframe->sequence_number; // reset warning... } } - if (config.output->delay) { current_delay = config.output->delay(); - if (current_delay==-1) { - debug(1,"Delay error when checking running latency."); - current_delay=0; + if (current_delay == -1) { + debug(1, "Delay error when checking running latency."); + current_delay = 0; } - if (current_delay<minimum_dac_queue_size) - minimum_dac_queue_size=current_delay; - - uint32_t bo = seq_diff(ab_read,ab_write); - - if (bo<minimum_buffer_occupancy) - minimum_buffer_occupancy=bo; - - if (bo>maximum_buffer_occupancy) - maximum_buffer_occupancy=bo; - - // this is the actual delay, including the latency we actually want, which will fluctuate a good bit about a potentially rising or falling trend. - int64_t delay = td_in_frames+rt-(nt-current_delay); - + if (current_delay < minimum_dac_queue_size) + minimum_dac_queue_size = current_delay; + + uint32_t bo = seq_diff(ab_read, ab_write); + + if (bo < minimum_buffer_occupancy) + minimum_buffer_occupancy = bo; + + if (bo > maximum_buffer_occupancy) + maximum_buffer_occupancy = bo; + + // this is the actual delay, including the latency we actually want, which will + // fluctuate a good bit about a potentially rising or falling trend. + int64_t delay = td_in_frames + rt - (nt - current_delay); + // This is the timing error for the next audio frame in the DAC. - sync_error = delay-config.latency; - + sync_error = delay - config.latency; + // before we finally commit to this frame, check its sequencing and timing - + // require a certain error before bothering to fix it... - if (sync_error>config.tolerance) { + if (sync_error > config.tolerance) { amount_to_stuff = -1; } - if (sync_error<-config.tolerance) { + if (sync_error < -config.tolerance) { amount_to_stuff = 1; } - + // only allow stuffing if there is enough time to do it -- check DAC buffer... - if (current_delay<DAC_BUFFER_QUEUE_MINIMUM_LENGTH) { + if (current_delay < DAC_BUFFER_QUEUE_MINIMUM_LENGTH) { // debug(1,"DAC buffer too short to allow stuffing."); - amount_to_stuff=0; + amount_to_stuff = 0; } // try to keep the corrections definitely below 1 in 1000 audio frames if (amount_to_stuff) { - uint32_t x = random()%1000; - if (x>352) - amount_to_stuff=0; + uint32_t x = random() % 1000; + if (x > 352) + amount_to_stuff = 0; } - - if ((amount_to_stuff==0) && (fix_volume==0x10000)) { + + if ((amount_to_stuff == 0) && (fix_volume == 0x10000)) { // if no stuffing needed and no volume adjustment, then - // don't send to stuff_buffer_* and don't copy to outbuf; just send directly to the output device... + // don't send to stuff_buffer_* and don't copy to outbuf; just send directly to the + // output device... config.output->play(inbuf, frame_size); } else { #ifdef HAVE_LIBSOXR switch (config.packet_stuffing) { - case ST_basic: -// if (amount_to_stuff) debug(1,"Basic stuff..."); - play_samples = stuff_buffer_basic(inbuf, outbuf,amount_to_stuff); - break; - case ST_soxr: -// if (amount_to_stuff) debug(1,"Soxr stuff..."); - play_samples = stuff_buffer_soxr(inbuf, outbuf,amount_to_stuff); - break; - } + case ST_basic: + // if (amount_to_stuff) debug(1,"Basic stuff..."); + play_samples = stuff_buffer_basic(inbuf, outbuf, amount_to_stuff); + break; + case ST_soxr: + // if (amount_to_stuff) debug(1,"Soxr stuff..."); + play_samples = stuff_buffer_soxr(inbuf, outbuf, amount_to_stuff); + break; + } #else -// if (amount_to_stuff) debug(1,"Standard stuff..."); - play_samples = stuff_buffer_basic(inbuf, outbuf,amount_to_stuff); + // if (amount_to_stuff) debug(1,"Standard stuff..."); + play_samples = stuff_buffer_basic(inbuf, outbuf, amount_to_stuff); #endif - /* - { - int co; - int is_silent=1; - short *p = outbuf; - for (co=0;co<play_samples;co++) { - if (*p!=0) - is_silent=0; - p++; - } - if (is_silent) - debug(1,"Silence!"); - } - */ + /* + { + int co; + int is_silent=1; + short *p = outbuf; + for (co=0;co<play_samples;co++) { + if (*p!=0) + is_silent=0; + p++; + } + if (is_silent) + debug(1,"Silence!"); + } + */ - config.output->play(outbuf, play_samples); + config.output->play(outbuf, play_samples); } - + // check for loss of sync // timestamp of zero means an inserted silent frame in place of a missing frame - if ((inframe->timestamp!=0) && (!please_stop) && (config.resyncthreshold!=0) && (abs(sync_error)>config.resyncthreshold)) { + if ((inframe->timestamp != 0) && (!please_stop) && (config.resyncthreshold != 0) && + (abs(sync_error) > config.resyncthreshold)) { sync_error_out_of_bounds++; - // debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld; timestamp: %llx, time now %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now); - if (sync_error_out_of_bounds>3) { - debug(1,"Lost sync with source for %d consecutive packets -- flushing and resyncing. Error: %lld.",sync_error_out_of_bounds,sync_error); + // debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld; + // timestamp: %llx, time now + // %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now); + if (sync_error_out_of_bounds > 3) { + debug(1, "Lost sync with source for %d consecutive packets -- flushing and " + "resyncing. Error: %lld.", + sync_error_out_of_bounds, sync_error); sync_error_out_of_bounds = 0; player_flush(nt); - } + } } else { sync_error_out_of_bounds = 0; } } else { // if there is no delay procedure, there can be no synchronising - if (fix_volume==0x10000) + if (fix_volume == 0x10000) config.output->play(inbuf, frame_size); else { - play_samples = stuff_buffer_basic(inbuf, outbuf,0); + play_samples = stuff_buffer_basic(inbuf, outbuf, 0); config.output->play(outbuf, frame_size); } } - // mark the frame as finished - inframe->timestamp=0; + // mark the frame as finished + inframe->timestamp = 0; inframe->sequence_number = 0; - + // debug(1,"Sync error %lld frames. Amount to stuff %d." ,sync_error,amount_to_stuff); - - // new stats calculation. We want a running average of sync error, drift, adjustment, number of additions+subtractions - - if (number_of_statistics==trend_interval) { + + // new stats calculation. We want a running average of sync error, drift, adjustment, + // number of additions+subtractions + + if (number_of_statistics == trend_interval) { // here we remove the oldest statistical data and take it from the summaries as well - tsum_of_sync_errors-=statistics[oldest_statistic].sync_error; + tsum_of_sync_errors -= statistics[oldest_statistic].sync_error; tsum_of_drifts -= statistics[oldest_statistic].drift; - if (statistics[oldest_statistic].correction>0) - tsum_of_insertions_and_deletions-=statistics[oldest_statistic].correction; + if (statistics[oldest_statistic].correction > 0) + tsum_of_insertions_and_deletions -= statistics[oldest_statistic].correction; else - tsum_of_insertions_and_deletions+=statistics[oldest_statistic].correction; - tsum_of_corrections-=statistics[oldest_statistic].correction; - oldest_statistic=(oldest_statistic+1)%trend_interval; - number_of_statistics--; + tsum_of_insertions_and_deletions += statistics[oldest_statistic].correction; + tsum_of_corrections -= statistics[oldest_statistic].correction; + oldest_statistic = (oldest_statistic + 1) % trend_interval; + number_of_statistics--; } - + statistics[newest_statistic].sync_error = sync_error; statistics[newest_statistic].correction = amount_to_stuff; - if (number_of_statistics==0) + if (number_of_statistics == 0) statistics[newest_statistic].drift = 0; else - statistics[newest_statistic].drift = sync_error-previous_sync_error-previous_correction; + statistics[newest_statistic].drift = + sync_error - previous_sync_error - previous_correction; previous_sync_error = sync_error; previous_correction = amount_to_stuff; tsum_of_sync_errors += sync_error; tsum_of_drifts += statistics[newest_statistic].drift; - if (amount_to_stuff>0) - tsum_of_insertions_and_deletions+=amount_to_stuff; + if (amount_to_stuff > 0) + tsum_of_insertions_and_deletions += amount_to_stuff; else - tsum_of_insertions_and_deletions-=amount_to_stuff; - tsum_of_corrections+=amount_to_stuff; - - newest_statistic=(newest_statistic+1)%trend_interval; + tsum_of_insertions_and_deletions -= amount_to_stuff; + tsum_of_corrections += amount_to_stuff; + + newest_statistic = (newest_statistic + 1) % trend_interval; number_of_statistics++; - } - if (play_number%print_interval==0) { - // we can now calculate running averages for sync error (frames), corrections (ppm), insertions plus deletions (ppm), drift (ppm) - double moving_average_sync_error = (1.0*tsum_of_sync_errors)/number_of_statistics; - double moving_average_correction = (1.0*tsum_of_corrections)/number_of_statistics; - double moving_average_insertions_plus_deletions = (1.0*tsum_of_insertions_and_deletions)/number_of_statistics; - double moving_average_drift = (1.0*tsum_of_drifts)/number_of_statistics; + if (play_number % print_interval == 0) { + // we can now calculate running averages for sync error (frames), corrections (ppm), + // insertions plus deletions (ppm), drift (ppm) + double moving_average_sync_error = (1.0 * tsum_of_sync_errors) / number_of_statistics; + double moving_average_correction = (1.0 * tsum_of_corrections) / number_of_statistics; + double moving_average_insertions_plus_deletions = + (1.0 * tsum_of_insertions_and_deletions) / number_of_statistics; + double moving_average_drift = (1.0 * tsum_of_drifts) / number_of_statistics; // if ((play_number/print_interval)%20==0) if (config.statistics_requested) if (at_least_one_frame_seen) - inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f (ppm); missing packets %llu; late packets %llu; too late packets %llu; resend requests %llu; min DAC queue size %lli, min and max buffer occupancy %u and %u.", moving_average_sync_error, moving_average_correction*1000000/352, moving_average_insertions_plus_deletions*1000000/352,missing_packets,late_packets,too_late_packets,resend_requests,minimum_dac_queue_size,minimum_buffer_occupancy,maximum_buffer_occupancy); + inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f " + "(ppm); missing packets %llu; late packets %llu; too late packets %llu; " + "resend requests %llu; min DAC queue size %lli, min and max buffer occupancy " + "%u and %u.", + moving_average_sync_error, moving_average_correction * 1000000 / 352, + moving_average_insertions_plus_deletions * 1000000 / 352, missing_packets, + late_packets, too_late_packets, resend_requests, minimum_dac_queue_size, + minimum_buffer_occupancy, maximum_buffer_occupancy); else inform("No frames received in the last sampling interval."); - minimum_dac_queue_size=1000000; // hack reset - maximum_buffer_occupancy = 0; // can't be less than this + minimum_dac_queue_size = 1000000; // hack reset + maximum_buffer_occupancy = 0; // can't be less than this minimum_buffer_occupancy = BUFFER_FRAMES; // can't be more than this at_least_one_frame_seen = 0; } @@ -1082,29 +1153,33 @@ static void *player_thread_func(void *arg) { return 0; } - // takes the volume as specified by the airplay protocol void player_volume(double f) { -// The volume ranges -144.0 (mute) or -30 -- 0. See http://git.zx2c4.com/Airtunes2/about/#setting-volume -// By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30 equal increments -// So, we will pass this on without any weighting if we have a hardware mixer, as we expect the mixer to be calibrated in dB. - -// Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to 65536 is about 48.1 dB (log10 of 65536 is 4.8164). -// Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate it back to a number. - - double scaled_volume = vol2attn(f,0,-4810); - double linear_volume = pow(10,scaled_volume/1000); - - if(f == -144.0) + // The volume ranges -144.0 (mute) or -30 -- 0. See + // http://git.zx2c4.com/Airtunes2/about/#setting-volume + // By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30 + // equal increments + // So, we will pass this on without any weighting if we have a hardware mixer, as we expect the + // mixer to be calibrated in dB. + + // Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to + // 65536 is about 48.1 dB (log10 of 65536 is 4.8164). + // Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate + // it back to a number. + + double scaled_volume = vol2attn(f, 0, -4810); + double linear_volume = pow(10, scaled_volume / 1000); + + if (f == -144.0) linear_volume = 0.0; - + if (config.output->volume) { - config.output->volume(f); // volume will be sent as metadata by the config.output device - linear_volume=1.0; // no attenuation needed -- this value is used as a flag to avoid calculations + config.output->volume(f); // volume will be sent as metadata by the config.output device + linear_volume = + 1.0; // no attenuation needed -- this value is used as a flag to avoid calculations } - - + if (config.output->parameters) config.output->parameters(&audio_information); else { @@ -1115,42 +1190,44 @@ void player_volume(double f) { audio_information.has_true_mute = 0; audio_information.is_muted = 0; } - audio_information.valid=1; + audio_information.valid = 1; pthread_mutex_lock(&vol_mutex); - software_mixer_volume = linear_volume; - fix_volume = 65536.0 * software_mixer_volume; + software_mixer_volume = linear_volume; + fix_volume = 65536.0 * software_mixer_volume; pthread_mutex_unlock(&vol_mutex); #ifdef CONFIG_METADATA char *dv = malloc(64); // will be freed in the metadata thread if (dv) { - memset(dv,0,64); - snprintf(dv,63,"%.2f,%.2f,%.2f,%.2f",audio_information.airplay_volume,audio_information.current_volume_dB/100.0,audio_information.minimum_volume_dB/100.0,audio_information.maximum_volume_dB/100.0); - send_ssnc_metadata('pvol',dv,strlen(dv),1); + memset(dv, 0, 64); + snprintf(dv, 63, "%.2f,%.2f,%.2f,%.2f", audio_information.airplay_volume, + audio_information.current_volume_dB / 100.0, + audio_information.minimum_volume_dB / 100.0, + audio_information.maximum_volume_dB / 100.0); + send_ssnc_metadata('pvol', dv, strlen(dv), 1); } #endif - } void player_flush(uint32_t timestamp) { - // debug(1,"Flush requested up to %u. It seems as if 0 is special.",timestamp); + // debug(1,"Flush requested up to %u. It seems as if 0 is special.",timestamp); pthread_mutex_lock(&flush_mutex); - flush_requested=1; - //if (timestamp!=0) - flush_rtp_timestamp=timestamp; // flush all packets up to (and including?) this + flush_requested = 1; + // if (timestamp!=0) + flush_rtp_timestamp = timestamp; // flush all packets up to (and including?) this pthread_mutex_unlock(&flush_mutex); #ifdef CONFIG_METADATA - send_ssnc_metadata('pfls',NULL,0,1); + send_ssnc_metadata('pfls', NULL, 0, 1); #endif } int player_play(stream_cfg *stream) { packet_count = 0; if (config.buffer_start_fill > BUFFER_FRAMES) - die("specified buffer starting fill %d > buffer size %d", - config.buffer_start_fill, BUFFER_FRAMES); + die("specified buffer starting fill %d > buffer size %d", config.buffer_start_fill, + BUFFER_FRAMES); #ifdef HAVE_LIBPOLARSSL - memset(&dctx,0,sizeof(aes_context)); + memset(&dctx, 0, sizeof(aes_context)); aes_setkey_dec(&dctx, stream->aeskey, 128); #endif @@ -1165,21 +1242,21 @@ int player_play(stream_cfg *stream) { please_stop = 0; command_start(); #ifdef CONFIG_METADATA - send_ssnc_metadata('pbeg',NULL,0,1); + send_ssnc_metadata('pbeg', NULL, 0, 1); #endif - - // set the flowcontrol condition variable to wait on a monotonic clock + +// set the flowcontrol condition variable to wait on a monotonic clock #ifdef COMPILE_FOR_LINUX pthread_condattr_t attr; pthread_condattr_init(&attr); - pthread_condattr_setclock( &attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it. - int rc = pthread_cond_init(&flowcontrol,&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it. + int rc = pthread_cond_init(&flowcontrol, &attr); #endif #ifdef COMPILE_FOR_OSX - int rc = pthread_cond_init(&flowcontrol,NULL); + int rc = pthread_cond_init(&flowcontrol, NULL); #endif if (rc) - debug(1,"Error initialising condition variable."); + debug(1, "Error initialising condition variable."); config.output->start(sampling_rate); pthread_create(&player_thread, NULL, player_thread_func, NULL); @@ -1191,7 +1268,7 @@ void player_stop(void) { pthread_cond_signal(&flowcontrol); // tell it to give up pthread_join(player_thread, NULL); #ifdef CONFIG_METADATA - send_ssnc_metadata('pend',NULL,0,1); + send_ssnc_metadata('pend', NULL, 0, 1); #endif config.output->stop(); command_stop(); @@ -1199,5 +1276,5 @@ void player_stop(void) { free_decoder(); int rc = pthread_cond_destroy(&flowcontrol); if (rc) - debug(1,"Error destroying condition variable."); + debug(1, "Error destroying condition variable."); } @@ -4,8 +4,8 @@ #include "audio.h" typedef struct { - uint8_t aesiv[16], aeskey[16]; - int32_t fmtp[12]; + uint8_t aesiv[16], aeskey[16]; + int32_t fmtp[12]; } stream_cfg; typedef uint16_t seq_t; @@ -19,6 +19,6 @@ void player_stop(void); void player_volume(double f); void player_flush(uint32_t timestamp); -void player_put_packet(seq_t seqno,uint32_t timestamp, uint8_t *data, int len); +void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len); #endif //_PLAYER_H @@ -41,10 +41,9 @@ #include "player.h" #include "rtp.h" - typedef struct { - uint32_t seconds; - uint32_t fraction; + uint32_t seconds; + uint32_t fraction; } ntp_timestamp; typedef struct time_ping_record { @@ -57,14 +56,14 @@ static int running = 0; static int please_shutdown; static char client_ip_string[INET6_ADDRSTRLEN]; // the ip string pointing to the client -static short client_ip_family; // AF_INET / AF_INET6 -static uint32_t client_active_remote; // used when you want to control the client... +static short client_ip_family; // AF_INET / AF_INET6 +static uint32_t client_active_remote; // used when you want to control the client... static SOCKADDR rtp_client_control_socket; // a socket pointing to the control port of the client -static SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client -static int audio_socket; // our local [server] audio socket -static int control_socket; // our local [server] control socket -static int timing_socket; // local timing socket +static SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client +static int audio_socket; // our local [server] audio socket +static int control_socket; // our local [server] control socket +static int timing_socket; // local timing socket static pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread; static uint32_t reference_timestamp; @@ -73,168 +72,175 @@ static uint64_t reference_timestamp_time; // debug variables static int request_sent; -#define time_ping_history 8 -#define time_ping_fudge_factor 100000 +#define time_ping_history 8 +#define time_ping_fudge_factor 100000 static uint8_t time_ping_count; struct time_ping_record time_pings[time_ping_history]; -//static struct timespec dtt; // dangerous -- this assumes that there will never be two timing request in flight at the same time -static uint64_t departure_time; // dangerous -- this assumes that there will never be two timing request in flight at the same time +// static struct timespec dtt; // dangerous -- this assumes that there will never be two timing +// request in flight at the same time +static uint64_t departure_time; // dangerous -- this assumes that there will never be two timing + // request in flight at the same time static pthread_mutex_t reference_time_mutex = PTHREAD_MUTEX_INITIALIZER; uint64_t static local_to_remote_time_difference; // used to switch between local and remote clocks static void *rtp_audio_receiver(void *arg) { - // we inherit the signal mask (SIGUSR1) - - int32_t last_seqno = -1; - uint8_t packet[2048], *pktp; - - ssize_t nread; - while (1) { - if (please_shutdown) - break; - nread = recv(audio_socket, packet, sizeof(packet), 0); - if (nread < 0) - break; - - ssize_t plen = nread; - uint8_t type = packet[1] & ~0x80; - if (type == 0x60 || type == 0x56) { // audio data / resend - pktp = packet; - if (type==0x56) { - pktp += 4; - plen -= 4; - } - seq_t seqno = ntohs(*(unsigned short *)(pktp+2)); - // increment last_seqno and see if it's the same as the incoming seqno - - if (last_seqno==-1) - last_seqno=seqno; - else { - last_seqno = (last_seqno+1)&0xffff; - if (seqno!=last_seqno) - debug(2,"RTP: Packets out of sequence: expected: %d, got %d.",last_seqno,seqno); - last_seqno=seqno; // reset warning... - } - uint32_t timestamp = ntohl(*(unsigned long *)(pktp+4)); - - //if (packet[1]&0x10) - // debug(1,"Audio packet Extension bit set."); - - pktp += 12; - plen -= 12; - - // check if packet contains enough content to be reasonable - if (plen >= 16) { - player_put_packet(seqno,timestamp, pktp, plen); - continue; - } - if (type == 0x56 && seqno == 0) { - debug(2, "resend-related request packet received, ignoring."); - continue; - } - debug(1, "Audio receiver -- Unknown RTP packet of type 0x%02X length %d seqno %d", type, nread, seqno); - } - warn("Audio receiver -- Unknown RTP packet of type 0x%02X length %d.", type, nread); + // we inherit the signal mask (SIGUSR1) + + int32_t last_seqno = -1; + uint8_t packet[2048], *pktp; + + ssize_t nread; + while (1) { + if (please_shutdown) + break; + nread = recv(audio_socket, packet, sizeof(packet), 0); + if (nread < 0) + break; + + ssize_t plen = nread; + uint8_t type = packet[1] & ~0x80; + if (type == 0x60 || type == 0x56) { // audio data / resend + pktp = packet; + if (type == 0x56) { + pktp += 4; + plen -= 4; + } + seq_t seqno = ntohs(*(unsigned short *)(pktp + 2)); + // increment last_seqno and see if it's the same as the incoming seqno + + if (last_seqno == -1) + last_seqno = seqno; + else { + last_seqno = (last_seqno + 1) & 0xffff; + if (seqno != last_seqno) + debug(2, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno); + last_seqno = seqno; // reset warning... + } + uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4)); + + // if (packet[1]&0x10) + // debug(1,"Audio packet Extension bit set."); + + pktp += 12; + plen -= 12; + + // check if packet contains enough content to be reasonable + if (plen >= 16) { + player_put_packet(seqno, timestamp, pktp, plen); + continue; + } + if (type == 0x56 && seqno == 0) { + debug(2, "resend-related request packet received, ignoring."); + continue; + } + debug(1, "Audio receiver -- Unknown RTP packet of type 0x%02X length %d seqno %d", type, + nread, seqno); } + warn("Audio receiver -- Unknown RTP packet of type 0x%02X length %d.", type, nread); + } - debug(1, "Audio receiver -- Server RTP thread interrupted. terminating."); - close(audio_socket); + debug(1, "Audio receiver -- Server RTP thread interrupted. terminating."); + close(audio_socket); - return NULL; + return NULL; } static void *rtp_control_receiver(void *arg) { - // we inherit the signal mask (SIGUSR1) - reference_timestamp=0; // nothing valid received yet - uint8_t packet[2048]; - struct timespec tn; - uint64_t remote_time_of_sync,local_time_now, remote_time_now; - uint32_t sync_rtp_timestamp,rtp_timestamp_less_latency; - ssize_t nread; - while (1) { - if (please_shutdown) - break; - nread = recv(control_socket, packet, sizeof(packet), 0); - local_time_now=get_absolute_time_in_fp(); -// clock_gettime(CLOCK_MONOTONIC,&tn); -// local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000; - - if (nread < 0) - break; - - ssize_t plen = nread; - if (packet[1] == 0xd4) { // sync data - /* - char obf[4096]; - char *obfp = obf; - int obfc; - for (obfc=0;obfc<plen;obfc++) { - sprintf(obfp,"%02X",packet[obfc]); - obfp+=2; - }; - *obfp=0; - debug(1,"Sync Packet Received: \"%s\"",obf); - */ - if (local_to_remote_time_difference) { // need a time packet to be interchanged first... - - remote_time_of_sync = (uint64_t)ntohl(*((uint32_t*)&packet[8]))<<32; - remote_time_of_sync += ntohl(*((uint32_t*)&packet[12])); - - // debug(1,"Remote Sync Time: %0llx.",remote_time_of_sync); - - rtp_timestamp_less_latency = ntohl(*((uint32_t*)&packet[4])); - sync_rtp_timestamp = ntohl(*((uint32_t*)&packet[16])); - - if (packet[0]&0x10) { - // if it's a packet right after a flush or resume - sync_rtp_timestamp += 352; // add frame_size -- can't see a reference to this anywhere, but it seems to get everything into sync. - // it's as if the first sync after a flush or resume is the timing of the next packet after the one whose RTP is given. Weird. - } - pthread_mutex_lock(&reference_time_mutex); - reference_timestamp_time = remote_time_of_sync-local_to_remote_time_difference; - reference_timestamp = sync_rtp_timestamp; - pthread_mutex_unlock(&reference_time_mutex); - // debug(1,"New Reference timestamp and timestamp time..."); - // get estimated remote time now - remote_time_now = local_time_now+local_to_remote_time_difference; - - //debug(1,"Sync Time is %lld us late (remote times).",((remote_time_now-remote_time_of_sync)*1000000)>>32); - //debug(1,"Sync Time is %lld us late (local times).",((local_time_now-reference_timestamp_time)*1000000)>>32); - } else { - debug(1,"Sync packet received before we got a timing packet back."); - } - } else - debug(1,"Control Port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread); - } + // we inherit the signal mask (SIGUSR1) + reference_timestamp = 0; // nothing valid received yet + uint8_t packet[2048]; + struct timespec tn; + uint64_t remote_time_of_sync, local_time_now, remote_time_now; + uint32_t sync_rtp_timestamp, rtp_timestamp_less_latency; + ssize_t nread; + while (1) { + if (please_shutdown) + break; + nread = recv(control_socket, packet, sizeof(packet), 0); + local_time_now = get_absolute_time_in_fp(); + // clock_gettime(CLOCK_MONOTONIC,&tn); + // local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000; + + if (nread < 0) + break; + + ssize_t plen = nread; + if (packet[1] == 0xd4) { // sync data + /* + char obf[4096]; + char *obfp = obf; + int obfc; + for (obfc=0;obfc<plen;obfc++) { + sprintf(obfp,"%02X",packet[obfc]); + obfp+=2; + }; + *obfp=0; + debug(1,"Sync Packet Received: \"%s\"",obf); + */ + if (local_to_remote_time_difference) { // need a time packet to be interchanged first... + + remote_time_of_sync = (uint64_t)ntohl(*((uint32_t *)&packet[8])) << 32; + remote_time_of_sync += ntohl(*((uint32_t *)&packet[12])); + + // debug(1,"Remote Sync Time: %0llx.",remote_time_of_sync); + + rtp_timestamp_less_latency = ntohl(*((uint32_t *)&packet[4])); + sync_rtp_timestamp = ntohl(*((uint32_t *)&packet[16])); + + if (packet[0] & 0x10) { + // if it's a packet right after a flush or resume + sync_rtp_timestamp += 352; // add frame_size -- can't see a reference to this anywhere, + // but it seems to get everything into sync. + // it's as if the first sync after a flush or resume is the timing of the next packet + // after the one whose RTP is given. Weird. + } + pthread_mutex_lock(&reference_time_mutex); + reference_timestamp_time = remote_time_of_sync - local_to_remote_time_difference; + reference_timestamp = sync_rtp_timestamp; + pthread_mutex_unlock(&reference_time_mutex); + // debug(1,"New Reference timestamp and timestamp time..."); + // get estimated remote time now + remote_time_now = local_time_now + local_to_remote_time_difference; + + // debug(1,"Sync Time is %lld us late (remote + // times).",((remote_time_now-remote_time_of_sync)*1000000)>>32); + // debug(1,"Sync Time is %lld us late (local + // times).",((local_time_now-reference_timestamp_time)*1000000)>>32); + } else { + debug(1, "Sync packet received before we got a timing packet back."); + } + } else + debug(1, "Control Port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread); + } - debug(1, "Control RTP thread interrupted. terminating."); - close(control_socket); + debug(1, "Control RTP thread interrupted. terminating."); + close(control_socket); - return NULL; + return NULL; } static void *rtp_timing_sender(void *arg) { - struct timing_request { + struct timing_request { char leader; char type; uint16_t seqno; uint32_t filler; - uint64_t origin,receive,transmit; + uint64_t origin, receive, transmit; }; - - uint64_t request_number=0; - - struct timing_request req; // *not* a standard RTCP NACK - + + uint64_t request_number = 0; + + struct timing_request req; // *not* a standard RTCP NACK + req.leader = 0x80; - req.type = 0xd2; // Timing request + req.type = 0xd2; // Timing request req.filler = 0; - req.seqno=htons(7); - + req.seqno = htons(7); + time_ping_count = 0; // we inherit the signal mask (SIGUSR1) @@ -245,392 +251,404 @@ static void *rtp_timing_sender(void *arg) { if (!running) die("rtp_timing_sender called without active stream!"); - //debug(1, "Requesting ntp timestamp exchange."); + // debug(1, "Requesting ntp timestamp exchange."); req.filler = 0; - req.origin = req.receive = req.transmit=0; + req.origin = req.receive = req.transmit = 0; -// clock_gettime(CLOCK_MONOTONIC,&dtt); - departure_time=get_absolute_time_in_fp(); - socklen_t msgsize = sizeof(struct sockaddr_in); + // clock_gettime(CLOCK_MONOTONIC,&dtt); + departure_time = get_absolute_time_in_fp(); + socklen_t msgsize = sizeof(struct sockaddr_in); #ifdef AF_INET6 - if (rtp_client_timing_socket.SAFAMILY==AF_INET6) { - msgsize = sizeof(struct sockaddr_in6); - } + if (rtp_client_timing_socket.SAFAMILY == AF_INET6) { + msgsize = sizeof(struct sockaddr_in6); + } #endif - if (sendto(timing_socket, &req, sizeof(req), 0, (struct sockaddr*)&rtp_client_timing_socket, msgsize)==-1) { - perror("Error sendto-ing to timing socket"); - } + if (sendto(timing_socket, &req, sizeof(req), 0, (struct sockaddr *)&rtp_client_timing_socket, + msgsize) == -1) { + perror("Error sendto-ing to timing socket"); + } request_number++; - if (request_number<=4) + if (request_number <= 4) usleep(500000); else sleep(3); } - debug(1, "rtp_timing_sender thread interrupted. terminating."); + debug(1, "rtp_timing_sender thread interrupted. terminating."); return NULL; } static void *rtp_timing_receiver(void *arg) { - // we inherit the signal mask (SIGUSR1) - uint8_t packet[2048], *pktp; - ssize_t nread; - pthread_t timer_requester; - pthread_create(&timer_requester, NULL, &rtp_timing_sender, NULL); -// struct timespec att; - uint64_t distant_receive_time,distant_transmit_time,arrival_time,return_time,transit_time,processing_time; - local_to_remote_time_jitters = 0; - local_to_remote_time_jitters_count = 0; - uint64_t l2rtd=0; - while (1) { - if (please_shutdown) - break; - nread = recv(timing_socket, packet, sizeof(packet), 0); - arrival_time=get_absolute_time_in_fp(); -// clock_gettime(CLOCK_MONOTONIC,&att); - - if (nread < 0) - break; - - ssize_t plen = nread; - //debug(1,"Packet Received on Timing Port."); - if (packet[1] == 0xd3) { // timing reply - /* - char obf[4096]; - char *obfp = obf; - int obfc; - for (obfc=0;obfc<plen;obfc++) { - sprintf(obfp,"%02X",packet[obfc]); - obfp+=2; - }; - *obfp=0; - //debug(1,"Timing Packet Received: \"%s\"",obf); - */ - - //arrival_time = ((uint64_t)att.tv_sec<<32)+((uint64_t)att.tv_nsec<<32)/1000000000; - //departure_time = ((uint64_t)dtt.tv_sec<<32)+((uint64_t)dtt.tv_nsec<<32)/1000000000; - - return_time = arrival_time-departure_time; - - // uint64_t rtus = (return_time*1000000)>>32; debug(1,"Time ping turnaround time: %lld us.",rtus); - - //distant_receive_time = ((uint64_t)ntohl(*((uint32_t*)&packet[16])))<<32+ntohl(*((uint32_t*)&packet[20])); - - distant_receive_time = (uint64_t)ntohl(*((uint32_t*)&packet[16]))<<32; - distant_receive_time += ntohl(*((uint32_t*)&packet[20])); - - //distant_transmit_time = ((uint64_t)ntohl(*((uint32_t*)&packet[24])))<<32+ntohl(*((uint32_t*)&packet[28])); - - distant_transmit_time = (uint64_t)ntohl(*((uint32_t*)&packet[24]))<<32; - distant_transmit_time += ntohl(*((uint32_t*)&packet[28])); - - processing_time = distant_transmit_time-distant_receive_time; - - // debug(1,"Return trip time: %lluuS, remote processing time: %lluuS.",(return_time*1000000)>>32,(processing_time*1000000)>>32); - - uint64_t local_time_by_remote_clock = distant_transmit_time+return_time/2; - - unsigned int cc; - for (cc=time_ping_history-1;cc>0;cc--) { - time_pings[cc]=time_pings[cc-1]; - time_pings[cc].dispersion = (time_pings[cc].dispersion*133)/100; // make the dispersions 'age' by this rational factor - } - time_pings[0].local_to_remote_difference = local_time_by_remote_clock-arrival_time; - time_pings[0].dispersion = return_time; - if (time_ping_count<time_ping_history) - time_ping_count++; - - - // now pick the timestamp with the lowest dispersion - uint64_t l2rtd = time_pings[0].local_to_remote_difference; - uint64_t tld = time_pings[0].dispersion; - for (cc=1;cc<time_ping_count;cc++) - if (time_pings[cc].dispersion<tld) { - l2rtd=time_pings[cc].local_to_remote_difference; - tld=time_pings[cc].dispersion; - } - int64_t ji; - - if (time_ping_count>1) { - if (l2rtd>local_to_remote_time_difference) { - local_to_remote_time_jitters=local_to_remote_time_jitters+l2rtd-local_to_remote_time_difference; - ji = l2rtd-local_to_remote_time_difference; - } else { - local_to_remote_time_jitters=local_to_remote_time_jitters+local_to_remote_time_difference-l2rtd; - ji = - (local_to_remote_time_difference-l2rtd); - } - local_to_remote_time_jitters_count+=1; + // we inherit the signal mask (SIGUSR1) + uint8_t packet[2048], *pktp; + ssize_t nread; + pthread_t timer_requester; + pthread_create(&timer_requester, NULL, &rtp_timing_sender, NULL); + // struct timespec att; + uint64_t distant_receive_time, distant_transmit_time, arrival_time, return_time, transit_time, + processing_time; + local_to_remote_time_jitters = 0; + local_to_remote_time_jitters_count = 0; + uint64_t l2rtd = 0; + while (1) { + if (please_shutdown) + break; + nread = recv(timing_socket, packet, sizeof(packet), 0); + arrival_time = get_absolute_time_in_fp(); + // clock_gettime(CLOCK_MONOTONIC,&att); + + if (nread < 0) + break; + + ssize_t plen = nread; + // debug(1,"Packet Received on Timing Port."); + if (packet[1] == 0xd3) { // timing reply + /* + char obf[4096]; + char *obfp = obf; + int obfc; + for (obfc=0;obfc<plen;obfc++) { + sprintf(obfp,"%02X",packet[obfc]); + obfp+=2; + }; + *obfp=0; + //debug(1,"Timing Packet Received: \"%s\"",obf); + */ + + // arrival_time = ((uint64_t)att.tv_sec<<32)+((uint64_t)att.tv_nsec<<32)/1000000000; + // departure_time = ((uint64_t)dtt.tv_sec<<32)+((uint64_t)dtt.tv_nsec<<32)/1000000000; + + return_time = arrival_time - departure_time; + + // uint64_t rtus = (return_time*1000000)>>32; debug(1,"Time ping turnaround time: %lld + // us.",rtus); + + // distant_receive_time = + // ((uint64_t)ntohl(*((uint32_t*)&packet[16])))<<32+ntohl(*((uint32_t*)&packet[20])); + + distant_receive_time = (uint64_t)ntohl(*((uint32_t *)&packet[16])) << 32; + distant_receive_time += ntohl(*((uint32_t *)&packet[20])); + + // distant_transmit_time = + // ((uint64_t)ntohl(*((uint32_t*)&packet[24])))<<32+ntohl(*((uint32_t*)&packet[28])); + + distant_transmit_time = (uint64_t)ntohl(*((uint32_t *)&packet[24])) << 32; + distant_transmit_time += ntohl(*((uint32_t *)&packet[28])); + + processing_time = distant_transmit_time - distant_receive_time; + + // debug(1,"Return trip time: %lluuS, remote processing time: + // %lluuS.",(return_time*1000000)>>32,(processing_time*1000000)>>32); + + uint64_t local_time_by_remote_clock = distant_transmit_time + return_time / 2; + + unsigned int cc; + for (cc = time_ping_history - 1; cc > 0; cc--) { + time_pings[cc] = time_pings[cc - 1]; + time_pings[cc].dispersion = (time_pings[cc].dispersion * 133) / + 100; // make the dispersions 'age' by this rational factor + } + time_pings[0].local_to_remote_difference = local_time_by_remote_clock - arrival_time; + time_pings[0].dispersion = return_time; + if (time_ping_count < time_ping_history) + time_ping_count++; + + // now pick the timestamp with the lowest dispersion + uint64_t l2rtd = time_pings[0].local_to_remote_difference; + uint64_t tld = time_pings[0].dispersion; + for (cc = 1; cc < time_ping_count; cc++) + if (time_pings[cc].dispersion < tld) { + l2rtd = time_pings[cc].local_to_remote_difference; + tld = time_pings[cc].dispersion; } - // uncomment below to print jitter between client's clock and oour clock - // int64_t rtus = (tld*1000000)>>32; ji = (ji*1000000)>>32; debug(1,"Choosing time difference with dispersion of %lld us with delta of %lld us",rtus,ji); + int64_t ji; - local_to_remote_time_difference=l2rtd; + if (time_ping_count > 1) { + if (l2rtd > local_to_remote_time_difference) { + local_to_remote_time_jitters = + local_to_remote_time_jitters + l2rtd - local_to_remote_time_difference; + ji = l2rtd - local_to_remote_time_difference; } else { - debug(1, "Timing port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread); + local_to_remote_time_jitters = + local_to_remote_time_jitters + local_to_remote_time_difference - l2rtd; + ji = -(local_to_remote_time_difference - l2rtd); + } + local_to_remote_time_jitters_count += 1; } + // uncomment below to print jitter between client's clock and oour clock + // int64_t rtus = (tld*1000000)>>32; ji = (ji*1000000)>>32; debug(1,"Choosing time difference + // with dispersion of %lld us with delta of %lld us",rtus,ji); + + local_to_remote_time_difference = l2rtd; + } else { + debug(1, "Timing port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread); } + } - debug(1, "Timing RTP thread interrupted. terminating."); - void *retval; - pthread_kill(timer_requester, SIGUSR1); - pthread_join(timer_requester, &retval); - debug(1,"Closed and terminated timer requester thread."); - debug(1, "Timing RTP thread terminated."); - close(timing_socket); + debug(1, "Timing RTP thread interrupted. terminating."); + void *retval; + pthread_kill(timer_requester, SIGUSR1); + pthread_join(timer_requester, &retval); + debug(1, "Closed and terminated timer requester thread."); + debug(1, "Timing RTP thread terminated."); + close(timing_socket); - return NULL; + return NULL; } -static int bind_port(SOCKADDR *remote,int *sock, int desired_port ) { - struct - addrinfo hints, *info; +static int bind_port(SOCKADDR *remote, int *sock, int desired_port) { + struct addrinfo hints, *info; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = remote->SAFAMILY; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = remote->SAFAMILY; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_PASSIVE; - - char buffer[10]; - snprintf(buffer, 10, "%d", desired_port); - - int ret = getaddrinfo(NULL,buffer, &hints, &info); + char buffer[10]; + snprintf(buffer, 10, "%d", desired_port); - if (ret < 0) - die("failed to get usable addrinfo?! %s.", gai_strerror(ret)); + int ret = getaddrinfo(NULL, buffer, &hints, &info); - *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP); - ret = bind(*sock, info->ai_addr, info->ai_addrlen); + if (ret < 0) + die("failed to get usable addrinfo?! %s.", gai_strerror(ret)); - freeaddrinfo(info); + *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP); + ret = bind(*sock, info->ai_addr, info->ai_addrlen); - if (ret < 0) - die("could not bind a UDP port!"); + freeaddrinfo(info); - int sport; - SOCKADDR local; - socklen_t local_len = sizeof(local); - getsockname(*sock, (struct sockaddr*)&local, &local_len); + if (ret < 0) + die("could not bind a UDP port!"); + + int sport; + SOCKADDR local; + socklen_t local_len = sizeof(local); + getsockname(*sock, (struct sockaddr *)&local, &local_len); #ifdef AF_INET6 - if (local.SAFAMILY == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&local; - sport = ntohs(sa6->sin6_port); - } else + if (local.SAFAMILY == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&local; + sport = ntohs(sa6->sin6_port); + } else #endif - { - struct sockaddr_in *sa = (struct sockaddr_in*)&local; - sport = ntohs(sa->sin_port); - } + { + struct sockaddr_in *sa = (struct sockaddr_in *)&local; + sport = ntohs(sa->sin_port); + } - return sport; + return sport; } +void rtp_setup(SOCKADDR *remote, int cport, int tport, uint32_t active_remote, int *lsport, + int *lcport, int *ltport) { + if (running) + die("rtp_setup called with active stream!"); -void rtp_setup(SOCKADDR *remote, int cport, int tport, uint32_t active_remote, int *lsport, int *lcport, int *ltport) { - if (running) - die("rtp_setup called with active stream!"); - - debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport); - - client_active_remote = active_remote; - - // print out what we know about the client - void *addr; - char *ipver; - int port; - char portstr[20]; - client_ip_family = remote->SAFAMILY; // keep information about the kind of ip of the client + debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport); + + client_active_remote = active_remote; + + // print out what we know about the client + void *addr; + char *ipver; + int port; + char portstr[20]; + client_ip_family = remote->SAFAMILY; // keep information about the kind of ip of the client #ifdef AF_INET6 - if (remote->SAFAMILY == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)remote; - addr = &(sa6->sin6_addr); - port = ntohs(sa6->sin6_port); - ipver = "IPv6"; - } + if (remote->SAFAMILY == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)remote; + addr = &(sa6->sin6_addr); + port = ntohs(sa6->sin6_port); + ipver = "IPv6"; + } #endif - if (remote->SAFAMILY == AF_INET) { - struct sockaddr_in *sa4 = (struct sockaddr_in*)remote; - addr = &(sa4->sin_addr); - port = ntohs(sa4->sin_port); - ipver = "IPv4"; - } - inet_ntop(remote->SAFAMILY,addr,client_ip_string,sizeof(client_ip_string)); // keep the client's ip number - debug(1,"Connection from %s: %s:%d",ipver,client_ip_string,port); - - - - // set up a the record of the remote's control socket - struct addrinfo hints; - struct addrinfo *servinfo; - - memset(&rtp_client_control_socket,0,sizeof(rtp_client_control_socket)); - memset(&hints,0, sizeof hints); - hints.ai_family = remote->SAFAMILY; - hints.ai_socktype = SOCK_DGRAM; - snprintf(portstr, 20, "%d", cport); - if (getaddrinfo(client_ip_string,portstr,&hints,&servinfo)!=0) - die("Can't get address of client's control port"); + if (remote->SAFAMILY == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)remote; + addr = &(sa4->sin_addr); + port = ntohs(sa4->sin_port); + ipver = "IPv4"; + } + inet_ntop(remote->SAFAMILY, addr, client_ip_string, + sizeof(client_ip_string)); // keep the client's ip number + debug(1, "Connection from %s: %s:%d", ipver, client_ip_string, port); + + // set up a the record of the remote's control socket + struct addrinfo hints; + struct addrinfo *servinfo; + + memset(&rtp_client_control_socket, 0, sizeof(rtp_client_control_socket)); + memset(&hints, 0, sizeof hints); + hints.ai_family = remote->SAFAMILY; + hints.ai_socktype = SOCK_DGRAM; + snprintf(portstr, 20, "%d", cport); + if (getaddrinfo(client_ip_string, portstr, &hints, &servinfo) != 0) + die("Can't get address of client's control port"); #ifdef AF_INET6 - if (servinfo->ai_family == AF_INET6) - memcpy(&rtp_client_control_socket,servinfo->ai_addr,sizeof(struct sockaddr_in6)); - else + if (servinfo->ai_family == AF_INET6) + memcpy(&rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); + else #endif - memcpy(&rtp_client_control_socket,servinfo->ai_addr,sizeof(struct sockaddr_in)); - freeaddrinfo(servinfo); - - // set up a the record of the remote's timing socket - memset(&rtp_client_timing_socket,0,sizeof(rtp_client_timing_socket)); - memset(&hints,0, sizeof hints); - hints.ai_family = remote->SAFAMILY; - hints.ai_socktype = SOCK_DGRAM; - snprintf(portstr, 20, "%d", tport); - if (getaddrinfo(client_ip_string,portstr,&hints,&servinfo)!=0) - die("Can't get address of client's timing port"); + memcpy(&rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); + freeaddrinfo(servinfo); + + // set up a the record of the remote's timing socket + memset(&rtp_client_timing_socket, 0, sizeof(rtp_client_timing_socket)); + memset(&hints, 0, sizeof hints); + hints.ai_family = remote->SAFAMILY; + hints.ai_socktype = SOCK_DGRAM; + snprintf(portstr, 20, "%d", tport); + if (getaddrinfo(client_ip_string, portstr, &hints, &servinfo) != 0) + die("Can't get address of client's timing port"); #ifdef AF_INET6 - if (servinfo->ai_family == AF_INET6) - memcpy(&rtp_client_timing_socket,servinfo->ai_addr,sizeof(struct sockaddr_in6)); - else + if (servinfo->ai_family == AF_INET6) + memcpy(&rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); + else #endif - memcpy(&rtp_client_timing_socket,servinfo->ai_addr,sizeof(struct sockaddr_in)); - freeaddrinfo(servinfo); - - // now, we open three sockets -- one for the audio stream, one for the timing and one for the control - - *lsport = bind_port(remote,&audio_socket,0); - *lcport = bind_port(remote,&control_socket,0); - *ltport = bind_port(remote,&timing_socket,0); - - debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport, *ltport); - - please_shutdown = 0; - reference_timestamp=0; - pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL); - pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL); - pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL); - - running = 1; - request_sent=0; + memcpy(&rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); + freeaddrinfo(servinfo); + + // now, we open three sockets -- one for the audio stream, one for the timing and one for the + // control + + *lsport = bind_port(remote, &audio_socket, 0); + *lcport = bind_port(remote, &control_socket, 0); + *ltport = bind_port(remote, &timing_socket, 0); + + debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport, + *ltport); + + please_shutdown = 0; + reference_timestamp = 0; + pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL); + pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL); + pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL); + + running = 1; + request_sent = 0; } -void get_reference_timestamp_stuff(uint32_t *timestamp,uint64_t *timestamp_time) { +void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time) { pthread_mutex_lock(&reference_time_mutex); - *timestamp=reference_timestamp; - *timestamp_time = reference_timestamp_time; + *timestamp = reference_timestamp; + *timestamp_time = reference_timestamp_time; pthread_mutex_unlock(&reference_time_mutex); } void clear_reference_timestamp(void) { pthread_mutex_lock(&reference_time_mutex); - reference_timestamp=0; - reference_timestamp_time=0; + reference_timestamp = 0; + reference_timestamp_time = 0; pthread_mutex_unlock(&reference_time_mutex); } void rtp_shutdown(void) { - if (!running) - die("rtp_shutdown called without active stream!"); - - debug(2, "shutting down RTP thread"); - please_shutdown = 1; - void *retval; - reference_timestamp=0; - pthread_kill(rtp_audio_thread, SIGUSR1); - pthread_join(rtp_audio_thread, &retval); - pthread_kill(rtp_control_thread, SIGUSR1); - pthread_join(rtp_control_thread, &retval); - pthread_kill(rtp_timing_thread, SIGUSR1); - pthread_join(rtp_timing_thread, &retval); - running = 0; + if (!running) + die("rtp_shutdown called without active stream!"); + + debug(2, "shutting down RTP thread"); + please_shutdown = 1; + void *retval; + reference_timestamp = 0; + pthread_kill(rtp_audio_thread, SIGUSR1); + pthread_join(rtp_audio_thread, &retval); + pthread_kill(rtp_control_thread, SIGUSR1); + pthread_join(rtp_control_thread, &retval); + pthread_kill(rtp_timing_thread, SIGUSR1); + pthread_join(rtp_timing_thread, &retval); + running = 0; } void rtp_request_resend(seq_t first, uint32_t count) { - if (running) { - if (!request_sent) { - debug(2, "requesting resend on %d packets starting at %u.",count,first); - request_sent=1; - } + if (running) { + if (!request_sent) { + debug(2, "requesting resend on %d packets starting at %u.", count, first); + request_sent = 1; + } - char req[8]; // *not* a standard RTCP NACK - req[0] = 0x80; - req[1] = 0x55|0x80; // Apple 'resend' - *(unsigned short *)(req+2) = htons(1); // our seqnum - *(unsigned short *)(req+4) = htons(first); // missed seqnum - *(unsigned short *)(req+6) = htons(count); // count - socklen_t msgsize = sizeof(struct sockaddr_in); + char req[8]; // *not* a standard RTCP NACK + req[0] = 0x80; + req[1] = 0x55 | 0x80; // Apple 'resend' + *(unsigned short *)(req + 2) = htons(1); // our seqnum + *(unsigned short *)(req + 4) = htons(first); // missed seqnum + *(unsigned short *)(req + 6) = htons(count); // count + socklen_t msgsize = sizeof(struct sockaddr_in); #ifdef AF_INET6 - if (rtp_client_timing_socket.SAFAMILY==AF_INET6) { - msgsize = sizeof(struct sockaddr_in6); - } + if (rtp_client_timing_socket.SAFAMILY == AF_INET6) { + msgsize = sizeof(struct sockaddr_in6); + } #endif - if (sendto(audio_socket, req, sizeof(req), 0, (struct sockaddr*)&rtp_client_control_socket,msgsize)==-1) { - perror("Error sendto-ing to audio socket"); - } - } else { - if (!request_sent) { - debug(2,"rtp_request_resend called without active stream!"); - request_sent=1; - } + if (sendto(audio_socket, req, sizeof(req), 0, (struct sockaddr *)&rtp_client_control_socket, + msgsize) == -1) { + perror("Error sendto-ing to audio socket"); + } + } else { + if (!request_sent) { + debug(2, "rtp_request_resend called without active stream!"); + request_sent = 1; } + } } void rtp_request_client_pause() { if (running) { - if (client_active_remote==0) { - debug(1,"Can't request a client pause: no valid active remote."); - } else { - // debug(1,"Send a client pause request to %s:3689 with active remote %u.",client_ip_string,client_active_remote); - - - struct addrinfo hints, *res; - int sockfd; - - char message[1000] , server_reply[2000]; - - // first, load up address structs with getaddrinfo(): - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - getaddrinfo(client_ip_string, "3689", &hints, &res); - - // make a socket: - - sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - if (sockfd == -1) { - die("Could not create socket"); - } - // debug(1,"Socket created"); - - // connect! - - if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { - die("connect failed. Error"); - } - // debug(1,"Connect successful"); - - sprintf(message,"GET /ctrl-int/1/pause HTTP/1.1\r\nHost: %s:3689\r\nActive-Remote: %u\r\n\r\n",client_ip_string,client_active_remote); - // debug(1,"Sending this message: \"%s\".",message); - - //Send some data - if( send(sockfd , message , strlen(message) , 0) < 0) { - debug(1,"Send failed"); - } - - //Receive a reply from the server - if( recv(sockfd , server_reply , 2000 , 0) < 0) { - debug(1,"recv failed"); - } - - // debug(1,"Server replied: \"%s\".",server_reply); - - if (strstr(server_reply,"HTTP/1.1 204 No Content")!=server_reply) - debug(1,"Client pause request failed."); - // debug(1,"Client pause request failed: \"%s\".",server_reply); - close(sockfd); + if (client_active_remote == 0) { + debug(1, "Can't request a client pause: no valid active remote."); + } else { + // debug(1,"Send a client pause request to %s:3689 with active remote + // %u.",client_ip_string,client_active_remote); + + struct addrinfo hints, *res; + int sockfd; + + char message[1000], server_reply[2000]; + + // first, load up address structs with getaddrinfo(): + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + getaddrinfo(client_ip_string, "3689", &hints, &res); + + // make a socket: + + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sockfd == -1) { + die("Could not create socket"); + } + // debug(1,"Socket created"); + + // connect! + + if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { + die("connect failed. Error"); + } + // debug(1,"Connect successful"); + + sprintf(message, + "GET /ctrl-int/1/pause HTTP/1.1\r\nHost: %s:3689\r\nActive-Remote: %u\r\n\r\n", + client_ip_string, client_active_remote); + // debug(1,"Sending this message: \"%s\".",message); + + // Send some data + if (send(sockfd, message, strlen(message), 0) < 0) { + debug(1, "Send failed"); + } + + // Receive a reply from the server + if (recv(sockfd, server_reply, 2000, 0) < 0) { + debug(1, "recv failed"); + } + + // debug(1,"Server replied: \"%s\".",server_reply); + + if (strstr(server_reply, "HTTP/1.1 204 No Content") != server_reply) + debug(1, "Client pause request failed."); + // debug(1,"Client pause request failed: \"%s\".",server_reply); + close(sockfd); } } else { - debug(1,"Request to pause non-existent play stream -- ignored."); + debug(1, "Request to pause non-existent play stream -- ignored."); } } @@ -5,13 +5,14 @@ #include "player.h" -void rtp_setup(SOCKADDR *remote, int controlport, int timingport, uint32_t active_remote, int *local_server_port, int *local_control_port, int *local_timing_port); +void rtp_setup(SOCKADDR *remote, int controlport, int timingport, uint32_t active_remote, + int *local_server_port, int *local_control_port, int *local_timing_port); void rtp_shutdown(void); void rtp_request_resend(seq_t first, uint32_t count); void rtp_request_client_pause(void); // ask the client to pause -void get_reference_timestamp_stuff(uint32_t *timestamp,uint64_t *timestamp_time); -void clear_reference_timestamp(void); +void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time); +void clear_reference_timestamp(void); uint64_t static local_to_remote_time_jitters; uint64_t static local_to_remote_time_jitters_count; @@ -76,7 +76,6 @@ static pthread_mutex_t play_lock = PTHREAD_MUTEX_INITIALIZER; // if a reference count is read as zero, it means the it's being deallocated. static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER; - // only one thread is allowed to use the player at once. // it monitors the request variable (at least when interrupted) static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -84,11 +83,11 @@ static int please_shutdown = 0; static pthread_t playing_thread = 0; typedef struct { - int fd; - stream_cfg stream; - SOCKADDR remote; - int running; - pthread_t thread; + int fd; + stream_cfg stream; + SOCKADDR remote; + int running; + pthread_t thread; } rtsp_conn_info; #ifdef CONFIG_METADATA @@ -96,17 +95,17 @@ typedef struct { pthread_mutex_t pc_queue_lock; pthread_cond_t pc_queue_item_added_signal; pthread_cond_t pc_queue_item_removed_signal; - size_t item_size; // number of bytes in each item - uint32_t count; // number of items in the queue + size_t item_size; // number of bytes in each item + uint32_t count; // number of items in the queue uint32_t capacity; // maximum number of items - uint32_t toq; // first item to take - uint32_t eoq; // free space at end of queue - void *items; // a pointer to where the items are actually stored - } pc_queue; // producer-consumer queue + uint32_t toq; // first item to take + uint32_t eoq; // free space at end of queue + void *items; // a pointer to where the items are actually stored +} pc_queue; // producer-consumer queue #endif typedef struct { - uint32_t referenceCount; // we might start using this... + uint32_t referenceCount; // we might start using this... int nheaders; char *name[16]; char *value[16]; @@ -127,10 +126,10 @@ typedef struct { uint32_t code; char *data; uint32_t length; - rtsp_message* carrier; + rtsp_message *carrier; } metadata_package; -void pc_queue_init(pc_queue* the_queue, char* items, size_t item_size, uint32_t number_of_items) { +void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items) { the_queue->item_size = item_size; the_queue->items = items; the_queue->count = 0; @@ -139,85 +138,86 @@ void pc_queue_init(pc_queue* the_queue, char* items, size_t item_size, uint32_t the_queue->eoq = 0; } -int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length,rtsp_message* carrier,int block); +int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, + int block); -int send_ssnc_metadata(uint32_t code,char *data,uint32_t length,int block) { - return send_metadata('ssnc',code,data,length,NULL,block); +int send_ssnc_metadata(uint32_t code, char *data, uint32_t length, int block) { + return send_metadata('ssnc', code, data, length, NULL, block); } -int pc_queue_add_item(pc_queue* the_queue,const void* the_stuff, int block) { +int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { int rc; if (the_queue) { - if (block==0) { + if (block == 0) { rc = pthread_mutex_trylock(&the_queue->pc_queue_lock); - if (rc==EBUSY) + if (rc == EBUSY) return EBUSY; } else rc = pthread_mutex_lock(&the_queue->pc_queue_lock); if (rc) - debug(1,"Error locking for pc_queue_add_item"); - while(the_queue->count==the_queue->capacity) { - rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal,&the_queue->pc_queue_lock); + debug(1, "Error locking for pc_queue_add_item"); + while (the_queue->count == the_queue->capacity) { + rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal, &the_queue->pc_queue_lock); if (rc) - debug(1,"Error waiting for item to be removed"); + debug(1, "Error waiting for item to be removed"); } uint32_t i = the_queue->eoq; - void * p = the_queue->items + the_queue->item_size*i; -// void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq; - memcpy(p,the_stuff,the_queue->item_size); - + void *p = the_queue->items + the_queue->item_size * i; + // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq; + memcpy(p, the_stuff, the_queue->item_size); + // update the pointer i++; - if (i==the_queue->capacity) + if (i == the_queue->capacity) // fold pointer if necessary - i=0; + i = 0; the_queue->eoq = i; the_queue->count++; - if (the_queue->count==the_queue->capacity) - debug(1,"pc_queue is full!"); + if (the_queue->count == the_queue->capacity) + debug(1, "pc_queue is full!"); rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal); if (rc) - debug(1,"Error signalling after pc_queue_add_item"); + debug(1, "Error signalling after pc_queue_add_item"); rc = pthread_mutex_unlock(&the_queue->pc_queue_lock); if (rc) - debug(1,"Error unlocking for pc_queue_add_item"); + debug(1, "Error unlocking for pc_queue_add_item"); } else { - debug(1,"Adding an item to a NULL queue"); + debug(1, "Adding an item to a NULL queue"); } return 0; } -int pc_queue_get_item(pc_queue* the_queue,void* the_stuff) { +int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { int rc; if (the_queue) { rc = pthread_mutex_lock(&the_queue->pc_queue_lock); if (rc) - debug(1,"Error locking for pc_queue_get_item"); - while(the_queue->count==0) { - rc = pthread_cond_wait(&the_queue->pc_queue_item_added_signal,&the_queue->pc_queue_lock); + debug(1, "Error locking for pc_queue_get_item"); + while (the_queue->count == 0) { + rc = pthread_cond_wait(&the_queue->pc_queue_item_added_signal, &the_queue->pc_queue_lock); if (rc) - debug(1,"Error waiting for item to be added"); + debug(1, "Error waiting for item to be added"); } uint32_t i = the_queue->toq; -// void * p = &the_queue->qbase + the_queue->item_size*the_queue->toq; - void * p = the_queue->items + the_queue->item_size*i; - memcpy(the_stuff,p,the_queue->item_size); - + // void * p = &the_queue->qbase + the_queue->item_size*the_queue->toq; + void *p = the_queue->items + the_queue->item_size * i; + memcpy(the_stuff, p, the_queue->item_size); + // update the pointer i++; - if (i==the_queue->capacity) + if (i == the_queue->capacity) // fold pointer if necessary - i=0; + i = 0; the_queue->toq = i; the_queue->count--; rc = pthread_cond_signal(&the_queue->pc_queue_item_removed_signal); if (rc) - debug(1,"Error signalling after pc_queue_removed_item"); + debug(1, "Error signalling after pc_queue_removed_item"); rc = pthread_mutex_unlock(&the_queue->pc_queue_lock); if (rc) - debug(1,"Error unlocking for pc_queue_get_item"); + debug(1, "Error unlocking for pc_queue_get_item"); } else { - debug(1,"Removing an item from a NULL queue"); + debug(1, "Removing an item from a NULL queue"); } return 0; } @@ -226,12 +226,12 @@ int pc_queue_get_item(pc_queue* the_queue,void* the_stuff) { // determine if we are the currently playing thread static inline int rtsp_playing(void) { - if (pthread_mutex_trylock(&playing_mutex)) { - return pthread_equal(playing_thread, pthread_self()); - } else { - pthread_mutex_unlock(&playing_mutex); - return 0; - } + if (pthread_mutex_trylock(&playing_mutex)) { + return pthread_equal(playing_thread, pthread_self()); + } else { + pthread_mutex_unlock(&playing_mutex); + return 0; + } } void rtsp_request_shutdown_stream(void) { @@ -239,569 +239,578 @@ void rtsp_request_shutdown_stream(void) { pthread_kill(playing_thread, SIGUSR1); } - static void rtsp_take_player(void) { - if (rtsp_playing()) - return; + if (rtsp_playing()) + return; - if (pthread_mutex_trylock(&playing_mutex)) { - debug(1, "shutting down playing thread."); - // XXX minor race condition between please_shutdown and signal delivery - please_shutdown = 1; - pthread_kill(playing_thread, SIGUSR1); - pthread_mutex_lock(&playing_mutex); - } - playing_thread = pthread_self(); // make us the currently-playing thread (why?) + if (pthread_mutex_trylock(&playing_mutex)) { + debug(1, "shutting down playing thread."); + // XXX minor race condition between please_shutdown and signal delivery + please_shutdown = 1; + pthread_kill(playing_thread, SIGUSR1); + pthread_mutex_lock(&playing_mutex); + } + playing_thread = pthread_self(); // make us the currently-playing thread (why?) } void rtsp_shutdown_stream(void) { - rtsp_take_player(); - pthread_mutex_unlock(&playing_mutex); + rtsp_take_player(); + pthread_mutex_unlock(&playing_mutex); } // keep track of the threads we have spawned so we can join() them static rtsp_conn_info **conns = NULL; static int nconns = 0; static void track_thread(rtsp_conn_info *conn) { - conns = realloc(conns, sizeof(rtsp_conn_info*) * (nconns + 1)); - conns[nconns] = conn; - nconns++; + conns = realloc(conns, sizeof(rtsp_conn_info *) * (nconns + 1)); + conns[nconns] = conn; + nconns++; } static void cleanup_threads(void) { - void *retval; - int i; - debug(2, "culling threads."); - for (i=0; i<nconns; ) { - if (conns[i]->running == 0) { - pthread_join(conns[i]->thread, &retval); - free(conns[i]); - debug(2, "one joined..."); - nconns--; - if (nconns) - conns[i] = conns[nconns]; - } else { - i++; - } + void *retval; + int i; + debug(2, "culling threads."); + for (i = 0; i < nconns;) { + if (conns[i]->running == 0) { + pthread_join(conns[i]->thread, &retval); + free(conns[i]); + debug(2, "one joined..."); + nconns--; + if (nconns) + conns[i] = conns[nconns]; + } else { + i++; } + } } // park a null at the line ending, and return the next line pointer // accept \r, \n, or \r\n static char *nextline(char *in, int inbuf) { - char *out = NULL; - while (inbuf) { - if (*in == '\r') { - *in++ = 0; - out = in; - } - if (*in == '\n') { - *in++ = 0; - out = in; - } + char *out = NULL; + while (inbuf) { + if (*in == '\r') { + *in++ = 0; + out = in; + } + if (*in == '\n') { + *in++ = 0; + out = in; + } - if (out) - break; + if (out) + break; - in++; - inbuf--; - } - return out; + in++; + inbuf--; + } + return out; } -static void msg_retain(rtsp_message * msg) { +static void msg_retain(rtsp_message *msg) { if (msg) { - int rc = pthread_mutex_lock(&reference_counter_lock); - if (rc) - debug(1,"Error %d locking reference counter lock"); - msg->referenceCount++; - rc = pthread_mutex_unlock(&reference_counter_lock); - if (rc) - debug(1,"Error %d unlocking reference counter lock"); + int rc = pthread_mutex_lock(&reference_counter_lock); + if (rc) + debug(1, "Error %d locking reference counter lock"); + msg->referenceCount++; + rc = pthread_mutex_unlock(&reference_counter_lock); + if (rc) + debug(1, "Error %d unlocking reference counter lock"); } else { - debug(1,"null rtsp_message pointer passed to retain"); + debug(1, "null rtsp_message pointer passed to retain"); } } -static rtsp_message * msg_init(void) { - rtsp_message *msg = malloc(sizeof(rtsp_message)); - memset(msg, 0, sizeof(rtsp_message)); - msg->referenceCount = 1; // from now on, any access to this must be protected with the lock - return msg; +static rtsp_message *msg_init(void) { + rtsp_message *msg = malloc(sizeof(rtsp_message)); + memset(msg, 0, sizeof(rtsp_message)); + msg->referenceCount = 1; // from now on, any access to this must be protected with the lock + return msg; } static int msg_add_header(rtsp_message *msg, char *name, char *value) { - if (msg->nheaders >= sizeof(msg->name)/sizeof(char*)) { - warn("too many headers?!"); - return 1; - } + if (msg->nheaders >= sizeof(msg->name) / sizeof(char *)) { + warn("too many headers?!"); + return 1; + } - msg->name[msg->nheaders] = strdup(name); - msg->value[msg->nheaders] = strdup(value); - msg->nheaders++; + msg->name[msg->nheaders] = strdup(name); + msg->value[msg->nheaders] = strdup(value); + msg->nheaders++; - return 0; + return 0; } static char *msg_get_header(rtsp_message *msg, char *name) { - int i; - for (i=0; i<msg->nheaders; i++) - if (!strcasecmp(msg->name[i], name)) - return msg->value[i]; - return NULL; + int i; + for (i = 0; i < msg->nheaders; i++) + if (!strcasecmp(msg->name[i], name)) + return msg->value[i]; + return NULL; } static void msg_print_debug_headers(rtsp_message *msg) { int i; - for (i=0; i<msg->nheaders; i++) { - debug(1," Type: \"%s\", content: \"%s\"",msg->name[i],msg->value[i]); + for (i = 0; i < msg->nheaders; i++) { + debug(1, " Type: \"%s\", content: \"%s\"", msg->name[i], msg->value[i]); } } static void msg_free(rtsp_message *msg) { - + if (msg) { int rc = pthread_mutex_lock(&reference_counter_lock); if (rc) - debug(1,"Error %d locking reference counter lock during msg_free()",rc); + debug(1, "Error %d locking reference counter lock during msg_free()", rc); msg->referenceCount--; rc = pthread_mutex_unlock(&reference_counter_lock); if (rc) - debug(1,"Error %d unlocking reference counter lock during msg_free()",rc); - if (msg->referenceCount==0) { + debug(1, "Error %d unlocking reference counter lock during msg_free()", rc); + if (msg->referenceCount == 0) { int i; - for (i=0; i<msg->nheaders; i++) { - free(msg->name[i]); - free(msg->value[i]); + for (i = 0; i < msg->nheaders; i++) { + free(msg->name[i]); + free(msg->value[i]); } if (msg->content) free(msg->content); free(msg); } // else { // debug(1,"rtsp_message reference count non-zero: %d!",msg->referenceCount); - //} + //} } else { - debug(1,"null rtsp_message pointer passed to msg_free()"); - } + debug(1, "null rtsp_message pointer passed to msg_free()"); + } } - static int msg_handle_line(rtsp_message **pmsg, char *line) { - rtsp_message *msg = *pmsg; + rtsp_message *msg = *pmsg; - if (!msg) { - msg = msg_init(); - *pmsg = msg; - char *sp, *p; + if (!msg) { + msg = msg_init(); + *pmsg = msg; + char *sp, *p; - // debug(1, "received request: %s", line); + // debug(1, "received request: %s", line); - p = strtok_r(line, " ", &sp); - if (!p) - goto fail; - strncpy(msg->method, p, sizeof(msg->method)-1); + p = strtok_r(line, " ", &sp); + if (!p) + goto fail; + strncpy(msg->method, p, sizeof(msg->method) - 1); - p = strtok_r(NULL, " ", &sp); - if (!p) - goto fail; + p = strtok_r(NULL, " ", &sp); + if (!p) + goto fail; - p = strtok_r(NULL, " ", &sp); - if (!p) - goto fail; - if (strcmp(p, "RTSP/1.0")) - goto fail; + p = strtok_r(NULL, " ", &sp); + if (!p) + goto fail; + if (strcmp(p, "RTSP/1.0")) + goto fail; - return -1; - } + return -1; + } - if (strlen(line)) { - char *p; - p = strstr(line, ": "); - if (!p) { - warn("bad header: >>%s<<", line); - goto fail; - } - *p = 0; - p += 2; - msg_add_header(msg, line, p); - debug(2, " %s: %s.", line, p); - return -1; - } else { - char *cl = msg_get_header(msg, "Content-Length"); - if (cl) - return atoi(cl); - else - return 0; + if (strlen(line)) { + char *p; + p = strstr(line, ": "); + if (!p) { + warn("bad header: >>%s<<", line); + goto fail; } + *p = 0; + p += 2; + msg_add_header(msg, line, p); + debug(2, " %s: %s.", line, p); + return -1; + } else { + char *cl = msg_get_header(msg, "Content-Length"); + if (cl) + return atoi(cl); + else + return 0; + } fail: - *pmsg = NULL; - msg_free(msg); - return 0; + *pmsg = NULL; + msg_free(msg); + return 0; } -static enum rtsp_read_request_response rtsp_read_request(int fd, rtsp_message** the_packet) { - enum rtsp_read_request_response reply=rtsp_read_request_response_ok; - ssize_t buflen = 512; - char *buf = malloc(buflen+1); - - rtsp_message *msg = NULL; - - ssize_t nread; - ssize_t inbuf = 0; - int msg_size = -1; - - while (msg_size < 0) { - if (please_shutdown) { - debug(1, "RTSP shutdown requested."); - reply = rtsp_read_request_response_shutdown_requested; - goto shutdown; - } - nread = read(fd, buf+inbuf, buflen - inbuf); - if (!nread) { - debug(1, "RTSP connection closed."); - reply = rtsp_read_request_response_shutdown_requested; - goto shutdown; - } - if (nread < 0) { - if (errno==EINTR) - continue; - perror("read failure"); - reply = rtsp_read_request_response_error; - goto shutdown; - } - inbuf += nread; +static enum rtsp_read_request_response rtsp_read_request(int fd, rtsp_message **the_packet) { + enum rtsp_read_request_response reply = rtsp_read_request_response_ok; + ssize_t buflen = 512; + char *buf = malloc(buflen + 1); - char *next; - while (msg_size < 0 && (next = nextline(buf, inbuf))) { - msg_size = msg_handle_line(&msg, buf); + rtsp_message *msg = NULL; - if (!msg) { - warn("no RTSP header received"); - reply = rtsp_read_request_response_bad_packet; - goto shutdown; - } + ssize_t nread; + ssize_t inbuf = 0; + int msg_size = -1; - inbuf -= next-buf; - if (inbuf) - memmove(buf, next, inbuf); - } + while (msg_size < 0) { + if (please_shutdown) { + debug(1, "RTSP shutdown requested."); + reply = rtsp_read_request_response_shutdown_requested; + goto shutdown; + } + nread = read(fd, buf + inbuf, buflen - inbuf); + if (!nread) { + debug(1, "RTSP connection closed."); + reply = rtsp_read_request_response_shutdown_requested; + goto shutdown; } + if (nread < 0) { + if (errno == EINTR) + continue; + perror("read failure"); + reply = rtsp_read_request_response_error; + goto shutdown; + } + inbuf += nread; - if (msg_size > buflen) { - buf = realloc(buf, msg_size); - if (!buf) { - warn("too much content"); - reply = rtsp_read_request_response_error; - goto shutdown; - } - buflen = msg_size; + char *next; + while (msg_size < 0 && (next = nextline(buf, inbuf))) { + msg_size = msg_handle_line(&msg, buf); + + if (!msg) { + warn("no RTSP header received"); + reply = rtsp_read_request_response_bad_packet; + goto shutdown; + } + + inbuf -= next - buf; + if (inbuf) + memmove(buf, next, inbuf); + } + } + + if (msg_size > buflen) { + buf = realloc(buf, msg_size); + if (!buf) { + warn("too much content"); + reply = rtsp_read_request_response_error; + goto shutdown; } + buflen = msg_size; + } + + uint64_t threshold_time = + get_absolute_time_in_fp() + ((uint64_t)5 << 32); // i.e. five seconds from now + int warning_message_sent = 0; + + const size_t max_read_chunk = 50000; + while (inbuf < msg_size) { + + // we are going to read the stream in chunks and time how long it takes to do so. + // If it's taking too long, (and we find out about it), we will send an error message as + // metadata - uint64_t threshold_time = get_absolute_time_in_fp() + ((uint64_t)5<<32); // i.e. five seconds from now - int warning_message_sent = 0; - - const size_t max_read_chunk = 50000; - while (inbuf < msg_size) { - - // we are going to read the stream in chunks and time how long it takes to do so. - // If it's taking too long, (and we find out about it), we will send an error message as metadata - - if (warning_message_sent==0) { - uint64_t time_now = get_absolute_time_in_fp(); - if (time_now>threshold_time) { // it's taking too long - debug(1,"Error receiving metadata from source -- transmission seems to be stalled."); + if (warning_message_sent == 0) { + uint64_t time_now = get_absolute_time_in_fp(); + if (time_now > threshold_time) { // it's taking too long + debug(1, "Error receiving metadata from source -- transmission seems to be stalled."); #ifdef CONFIG_METADATA - send_ssnc_metadata('stal',NULL,0,1); + send_ssnc_metadata('stal', NULL, 0, 1); #endif - warning_message_sent = 1; - } - } - ssize_t read_chunk = msg_size-inbuf; - if (read_chunk > max_read_chunk) - read_chunk = max_read_chunk; - nread = read(fd, buf+inbuf, read_chunk); - if (!nread) { - reply = rtsp_read_request_response_error; - goto shutdown; + warning_message_sent = 1; } - if (nread==EINTR) - continue; - if (nread < 0) { - perror("read failure"); - reply = rtsp_read_request_response_error; - goto shutdown; - } - inbuf += nread; } + ssize_t read_chunk = msg_size - inbuf; + if (read_chunk > max_read_chunk) + read_chunk = max_read_chunk; + nread = read(fd, buf + inbuf, read_chunk); + if (!nread) { + reply = rtsp_read_request_response_error; + goto shutdown; + } + if (nread == EINTR) + continue; + if (nread < 0) { + perror("read failure"); + reply = rtsp_read_request_response_error; + goto shutdown; + } + inbuf += nread; + } - msg->contentlength = inbuf; - msg->content = buf; - *the_packet = msg; - return reply; + msg->contentlength = inbuf; + msg->content = buf; + *the_packet = msg; + return reply; shutdown: - if (msg) { - msg_free(msg); // which will free the content and everything else - } - // in case the message wasn't formed or wasn't fully initialised - if ((msg) && (msg-> content == NULL) || (!msg)) - free(buf); - *the_packet = NULL; - return reply; + if (msg) { + msg_free(msg); // which will free the content and everything else + } + // in case the message wasn't formed or wasn't fully initialised + if ((msg) && (msg->content == NULL) || (!msg)) + free(buf); + *the_packet = NULL; + return reply; } static void msg_write_response(int fd, rtsp_message *resp) { - char pkt[1024]; - int pktfree = sizeof(pkt); - char *p = pkt; - int i, n; - - n = snprintf(p, pktfree, - "RTSP/1.0 %d %s\r\n", resp->respcode, - resp->respcode==200 ? "OK" : "Error"); - // debug(1, "sending response: %s", pkt); + char pkt[1024]; + int pktfree = sizeof(pkt); + char *p = pkt; + int i, n; + + n = snprintf(p, pktfree, "RTSP/1.0 %d %s\r\n", resp->respcode, + resp->respcode == 200 ? "OK" : "Error"); + // debug(1, "sending response: %s", pkt); + pktfree -= n; + p += n; + + for (i = 0; i < resp->nheaders; i++) { + debug(2, " %s: %s.", resp->name[i], resp->value[i]); + n = snprintf(p, pktfree, "%s: %s\r\n", resp->name[i], resp->value[i]); pktfree -= n; p += n; + if (pktfree <= 0) + die("Attempted to write overlong RTSP packet"); + } - for (i=0; i<resp->nheaders; i++) { - debug(2, " %s: %s.", resp->name[i], resp->value[i]); - n = snprintf(p, pktfree, "%s: %s\r\n", resp->name[i], resp->value[i]); - pktfree -= n; - p += n; - if (pktfree <= 0) - die("Attempted to write overlong RTSP packet"); - } - - if (pktfree < 3) - die("Attempted to write overlong RTSP packet"); + if (pktfree < 3) + die("Attempted to write overlong RTSP packet"); - strcpy(p, "\r\n"); - int ignore = write(fd, pkt, p-pkt+2); + strcpy(p, "\r\n"); + int ignore = write(fd, pkt, p - pkt + 2); } -static void handle_record(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { +static void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { resp->respcode = 200; - msg_add_header(resp, "Audio-Latency","88200"); + msg_add_header(resp, "Audio-Latency", "88200"); } -static void handle_options(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - resp->respcode = 200; - msg_add_header(resp, "Public", - "ANNOUNCE, SETUP, RECORD, " - "PAUSE, FLUSH, TEARDOWN, " - "OPTIONS, GET_PARAMETER, SET_PARAMETER"); +static void handle_options(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + resp->respcode = 200; + msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, " + "PAUSE, FLUSH, TEARDOWN, " + "OPTIONS, GET_PARAMETER, SET_PARAMETER"); } -static void handle_teardown(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - if (!rtsp_playing()) - return; - resp->respcode = 200; - msg_add_header(resp, "Connection", "close"); - please_shutdown = 1; +static void handle_teardown(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + if (!rtsp_playing()) + return; + resp->respcode = 200; + msg_add_header(resp, "Connection", "close"); + please_shutdown = 1; } -static void handle_flush(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - if (!rtsp_playing()) - return; - char *p; - uint32_t rtptime=0; - char * hdr = msg_get_header(req,"RTP-Info"); - - if (hdr) { - // debug(1,"FLUSH message received: \"%s\".",hdr); - // get the rtp timestamp - p = strstr(hdr, "rtptime="); - if (p) { - p = strchr(p, '=') + 1; - if (p) - rtptime = uatoi(p); // unsigned integer -- up to 2^32-1 - } +static void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + if (!rtsp_playing()) + return; + char *p; + uint32_t rtptime = 0; + char *hdr = msg_get_header(req, "RTP-Info"); + + if (hdr) { + // debug(1,"FLUSH message received: \"%s\".",hdr); + // get the rtp timestamp + p = strstr(hdr, "rtptime="); + if (p) { + p = strchr(p, '=') + 1; + if (p) + rtptime = uatoi(p); // unsigned integer -- up to 2^32-1 } - // debug(1,"RTSP Flush Requested: %u.",rtptime); - player_flush(rtptime); - resp->respcode = 200; + } + // debug(1,"RTSP Flush Requested: %u.",rtptime); + player_flush(rtptime); + resp->respcode = 200; } -static void handle_setup(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - int cport, tport; - int lsport,lcport,ltport; - uint32_t active_remote=0; - - char * ar = msg_get_header(req,"Active-Remote"); - if (ar) { - // debug(1,"Active-Remote string seen: \"%s\".",ar); - // get the active remote - char *p; - active_remote = strtoul(ar,&p,10); - // debug(1,"Active Remote is %u.",active_remote); - } - - // select latency - // if iTunes V10 or later is detected, use the iTunes latency setting - // if AirPlay is detected, use the AirPlay latency setting - // for everything else, use the general latency setting, if given, or - // else use the default latency setting - - config.latency=88200; - - if (config.userSuppliedLatency) - config.latency=config.userSuppliedLatency; - - char * ua = msg_get_header(req,"User-Agent"); - if (ua==0) { - debug(1,"No User-Agent string found in the SETUP message. Using latency of %d frames.",config.latency); - } else { - if (strstr(ua,"iTunes")==ua) { - int iTunesVersion=0; - // now check it's version 10 or later - char *pp = strchr(ua,'/') + 1; - if (pp) - iTunesVersion=atoi(pp); - else - debug(2,"iTunes Version Number not found."); - if (iTunesVersion>=10) { - debug(2,"User-Agent is iTunes 10 or better, (actual version is %d); selecting the iTunes latency of %d frames.",iTunesVersion,config.iTunesLatency); - config.latency=config.iTunesLatency; - } - } else if (strstr(ua,"AirPlay")==ua) { - debug(2,"User-Agent is AirPlay; selecting the AirPlay latency of %d frames.",config.AirPlayLatency); - config.latency=config.AirPlayLatency; - } else if (strstr(ua,"forked-daapd")==ua) { - debug(2,"User-Agent is forked-daapd; selecting the forked-daapd latency of %d frames.",config.ForkedDaapdLatency); - config.latency=config.ForkedDaapdLatency; - } else { - debug(2,"Unrecognised User-Agent. Using latency of %d frames.",config.latency); - } - } - char *hdr = msg_get_header(req, "Transport"); - if (!hdr) - goto error; +static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + int cport, tport; + int lsport, lcport, ltport; + uint32_t active_remote = 0; + char *ar = msg_get_header(req, "Active-Remote"); + if (ar) { + // debug(1,"Active-Remote string seen: \"%s\".",ar); + // get the active remote char *p; - p = strstr(hdr, "control_port="); - if (!p) - goto error; - p = strchr(p, '=') + 1; - cport = atoi(p); + active_remote = strtoul(ar, &p, 10); + // debug(1,"Active Remote is %u.",active_remote); + } - p = strstr(hdr, "timing_port="); - if (!p) - goto error; - p = strchr(p, '=') + 1; - tport = atoi(p); - - rtsp_take_player(); - rtp_setup(&conn->remote, cport, tport, active_remote, &lsport,&lcport,<port); - if (!lsport) - goto error; - char *q; - p = strstr(hdr,"control_port="); - if (p) { - q = strchr(p,';'); // get past the control port entry - *p++=0; - if (q++) - strcat(hdr,q); // should unsplice the control port entry - } - p = strstr(hdr,"timing_port="); - if (p) { - q = strchr(p,';'); // get past the timing port entry - *p++=0; - if (q++) - strcat(hdr,q); // should unsplice the timing port entry + // select latency + // if iTunes V10 or later is detected, use the iTunes latency setting + // if AirPlay is detected, use the AirPlay latency setting + // for everything else, use the general latency setting, if given, or + // else use the default latency setting + + config.latency = 88200; + + if (config.userSuppliedLatency) + config.latency = config.userSuppliedLatency; + + char *ua = msg_get_header(req, "User-Agent"); + if (ua == 0) { + debug(1, "No User-Agent string found in the SETUP message. Using latency of %d frames.", + config.latency); + } else { + if (strstr(ua, "iTunes") == ua) { + int iTunesVersion = 0; + // now check it's version 10 or later + char *pp = strchr(ua, '/') + 1; + if (pp) + iTunesVersion = atoi(pp); + else + debug(2, "iTunes Version Number not found."); + if (iTunesVersion >= 10) { + debug(2, "User-Agent is iTunes 10 or better, (actual version is %d); selecting the iTunes " + "latency of %d frames.", + iTunesVersion, config.iTunesLatency); + config.latency = config.iTunesLatency; + } + } else if (strstr(ua, "AirPlay") == ua) { + debug(2, "User-Agent is AirPlay; selecting the AirPlay latency of %d frames.", + config.AirPlayLatency); + config.latency = config.AirPlayLatency; + } else if (strstr(ua, "forked-daapd") == ua) { + debug(2, "User-Agent is forked-daapd; selecting the forked-daapd latency of %d frames.", + config.ForkedDaapdLatency); + config.latency = config.ForkedDaapdLatency; + } else { + debug(2, "Unrecognised User-Agent. Using latency of %d frames.", config.latency); } - - player_play(&conn->stream); + } + char *hdr = msg_get_header(req, "Transport"); + if (!hdr) + goto error; + + char *p; + p = strstr(hdr, "control_port="); + if (!p) + goto error; + p = strchr(p, '=') + 1; + cport = atoi(p); + + p = strstr(hdr, "timing_port="); + if (!p) + goto error; + p = strchr(p, '=') + 1; + tport = atoi(p); + + rtsp_take_player(); + rtp_setup(&conn->remote, cport, tport, active_remote, &lsport, &lcport, <port); + if (!lsport) + goto error; + char *q; + p = strstr(hdr, "control_port="); + if (p) { + q = strchr(p, ';'); // get past the control port entry + *p++ = 0; + if (q++) + strcat(hdr, q); // should unsplice the control port entry + } + p = strstr(hdr, "timing_port="); + if (p) { + q = strchr(p, ';'); // get past the timing port entry + *p++ = 0; + if (q++) + strcat(hdr, q); // should unsplice the timing port entry + } - char *resphdr = alloca(200); - *resphdr=0; - sprintf(resphdr, "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d;server_port=%d", lcport, ltport, lsport); + player_play(&conn->stream); - msg_add_header(resp, "Transport", resphdr); + char *resphdr = alloca(200); + *resphdr = 0; + sprintf(resphdr, "RTP/AVP/" + "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d;server_" + "port=%d", + lcport, ltport, lsport); - msg_add_header(resp, "Session", "1"); + msg_add_header(resp, "Transport", resphdr); - resp->respcode = 200; - return; + msg_add_header(resp, "Session", "1"); + + resp->respcode = 200; + return; error: - warn("Error in setup request."); - pthread_mutex_unlock(&play_lock); - resp->respcode = 451; // invalid arguments + warn("Error in setup request."); + pthread_mutex_unlock(&play_lock); + resp->respcode = 451; // invalid arguments } -static void handle_ignore(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - resp->respcode = 200; +static void handle_ignore(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + resp->respcode = 200; } -static void handle_set_parameter_parameter(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - char *cp = req->content; - int cp_left = req->contentlength; - char *next; - while (cp_left && cp) { - next = nextline(cp, cp_left); - cp_left -= next-cp; - - if (!strncmp(cp, "volume: ", 8)) { - if (config.ignore_volume_control==0) { - float volume = atof(cp + 8); - debug(2, "volume: %f\n", volume); - player_volume(volume); - } - } else -#ifdef CONFIG_METADATA - if(!strncmp(cp, "progress: ", 10)) { - char *progress = cp + 10; - debug(2, "progress: \"%s\"\n", progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second - send_ssnc_metadata('prgr',strdup(progress),strlen(progress),1); - } else -#endif - { - debug(1, "unrecognised parameter: \"%s\" (%d)\n", cp, strlen(cp)); - } - cp = next; +static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req, + rtsp_message *resp) { + char *cp = req->content; + int cp_left = req->contentlength; + char *next; + while (cp_left && cp) { + next = nextline(cp, cp_left); + cp_left -= next - cp; + + if (!strncmp(cp, "volume: ", 8)) { + if (config.ignore_volume_control == 0) { + float volume = atof(cp + 8); + debug(2, "volume: %f\n", volume); + player_volume(volume); + } + } else +#ifdef CONFIG_METADATA + if (!strncmp(cp, "progress: ", 10)) { + char *progress = cp + 10; + debug(2, "progress: \"%s\"\n", + progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second + send_ssnc_metadata('prgr', strdup(progress), strlen(progress), 1); + } else +#endif + { + debug(1, "unrecognised parameter: \"%s\" (%d)\n", cp, strlen(cp)); } + cp = next; + } } - #ifdef CONFIG_METADATA // Metadata is not used by shairport-sync. -// Instead we send all metadata to a fifo pipe, so that other apps can listen to the pipe and use the metadata. +// Instead we send all metadata to a fifo pipe, so that other apps can listen to the pipe and use +// the metadata. -// We use two 4-character codes to identify each piece of data and we send the data itself, if any, in base64 form. +// We use two 4-character codes to identify each piece of data and we send the data itself, if any, +// in base64 form. // The first 4-character code, called the "type", is either: -// 'core' for all the regular metadadata coming from iTunes, etc., or -// 'ssnc' (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as start/end delimiters, etc. +// 'core' for all the regular metadadata coming from iTunes, etc., or +// 'ssnc' (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as +// start/end delimiters, etc. -// For 'core' metadata, the second 4-character code is the 4-character metadata code coming from iTunes etc. +// For 'core' metadata, the second 4-character code is the 4-character metadata code coming from +// iTunes etc. // For 'ssnc' metadata, the second 4-character code is used to distinguish the messages. -// Cover art is not tagged in the same way as other metadata, it seems, so is sent as an 'ssnc' type metadata message with the code 'PICT' +// Cover art is not tagged in the same way as other metadata, it seems, so is sent as an 'ssnc' type +// metadata message with the code 'PICT' // Here are the 'ssnc' codes defined so far: -// 'PICT' -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see which. +// 'PICT' -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see +// which. // 'pbeg' -- play stream begin. No arguments // 'pend' -- play stream end. No arguments // 'pfls' -- play stream flush. No arguments // 'prsm' -- play stream resume. No arguments -// 'pvol' -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume,has_true_mute,is_muted" +// 'pvol' -- play volume. The volume is sent as a string -- +// "airplay_volume,volume,lowest_volume,highest_volume,has_true_mute,is_muted" // volume, lowest_volume and highest_volume are given in dB -// is_muted is 1 if [true] mute is enabled, 0 otherwise. -// The "airplay_volume" is what's sent to the player, and is from 0.00 down to -30.00, with -144.00 meaning mute. +// is_muted is 1 if [true] mute is enabled, 0 otherwise. +// The "airplay_volume" is what's sent to the player, and is from 0.00 down to -30.00, +// with -144.00 meaning mute. // This is linear on the volume control slider of iTunes or iOS AirPLay -// 'prgr' -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start of the current play sequence, the current play point and the end of the play sequence. +// 'prgr' -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start +// of the current play sequence, the current play point and the end of the play sequence. // I guess the timestamps wrap at 2^32. // 'mdst' -- a sequence of metadata is about to start // 'mden' -- a sequence of metadata has ended // 'snam' -- the name of the originator -- e.g. "Joe's iPhone" or "iTunes...". -// +// // including a simple base64 encoder to minimise malloc/free activity // From Stack Overflow, with thanks: @@ -812,49 +821,45 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn, // add _so to end of name to avoid confusion with polarssl's implementation -static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/'}; +static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static int mod_table[] = {0, 2, 1}; -// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int containing its maximum length +// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int +// containing its maximum length // the actual length will be returned. -char *base64_encode_so(const unsigned char *data, - size_t input_length, - char *encoded_data, - size_t *output_length) { - - size_t calculated_output_length = 4 * ((input_length + 2) / 3); - if (calculated_output_length> *output_length) - return(NULL); - *output_length = calculated_output_length; - - int i,j; - for (i = 0, j = 0; i < input_length;) { - - uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; - uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; - uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; - - uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; - - encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; - } +char *base64_encode_so(const unsigned char *data, size_t input_length, char *encoded_data, + size_t *output_length) { + + size_t calculated_output_length = 4 * ((input_length + 2) / 3); + if (calculated_output_length > *output_length) + return (NULL); + *output_length = calculated_output_length; + + int i, j; + for (i = 0, j = 0; i < input_length;) { - for (i = 0; i < mod_table[input_length % 3]; i++) - encoded_data[*output_length - 1 - i] = '='; + uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } - return encoded_data; + for (i = 0; i < mod_table[input_length % 3]; i++) + encoded_data[*output_length - 1 - i] = '='; + + return encoded_data; } // with thanks! @@ -869,68 +874,67 @@ metadata_package metadata_queue_items[metadata_queue_size]; static pthread_t metadata_thread; void metadata_create(void) { - if (config.metadata_enabled==0) - return; + if (config.metadata_enabled == 0) + return; - size_t pl = strlen(config.metadata_pipename) + 1; + size_t pl = strlen(config.metadata_pipename) + 1; - char* path = malloc(pl+1); - snprintf(path, pl+1, "%s", config.metadata_pipename); + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); - if (mkfifo(path, 0644) && errno != EEXIST) - die("Could not create metadata FIFO %s", path); + if (mkfifo(path, 0644) && errno != EEXIST) + die("Could not create metadata FIFO %s", path); - free(path); + free(path); } void metadata_open(void) { - if (config.metadata_enabled==0) - return; + if (config.metadata_enabled == 0) + return; - size_t pl = strlen(config.metadata_pipename) + 1; + size_t pl = strlen(config.metadata_pipename) + 1; - char* path = malloc(pl+1); - snprintf(path, pl+1, "%s", config.metadata_pipename); + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); - fd = open(path, O_WRONLY | O_NONBLOCK); - //if (fd < 0) - // debug(1, "Could not open metadata FIFO %s. Will try again later.", path); + fd = open(path, O_WRONLY | O_NONBLOCK); + // if (fd < 0) + // debug(1, "Could not open metadata FIFO %s. Will try again later.", path); - free(path); + free(path); } static void metadata_close(void) { - close(fd); - fd = -1; + close(fd); + fd = -1; } ssize_t non_blocking_write(int fd, const void *buf, size_t count) { // debug(1,"writing %u to pipe...",count); // we are assuming that the count is always smaller than the FIFO's buffer struct pollfd ufds[1]; - ssize_t reply; + ssize_t reply; do { - ufds[0].fd=fd; + ufds[0].fd = fd; ufds[0].events = POLLOUT; - int rv = poll(ufds,1,5000); - if (rv==-1) - debug(1,"error waiting for pipe to unblock..."); - if (rv==0) - debug(1,"timeout waiting for pipe to unblock"); - reply=write(fd,buf,count); - if ((reply==-1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - debug(1,"writing to pipe will block..."); -// else -// debug(1,"writing %u to pipe done...",reply); - } while ((reply==-1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))); + int rv = poll(ufds, 1, 5000); + if (rv == -1) + debug(1, "error waiting for pipe to unblock..."); + if (rv == 0) + debug(1, "timeout waiting for pipe to unblock"); + reply = write(fd, buf, count); + if ((reply == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + debug(1, "writing to pipe will block..."); + // else + // debug(1,"writing %u to pipe done...",reply); + } while ((reply == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))); return reply; - -// return write(fd,buf,count); + // return write(fd,buf,count); } -void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) { - debug(2,"Process metadata with type %x, code %x and length %u.",type,code,length); +void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) { + debug(2, "Process metadata with type %x, code %x and length %u.", type, code, length); int ret; // readers may go away and come back if (fd < 0) @@ -938,14 +942,15 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) { if (fd < 0) return; char thestring[1024]; - snprintf(thestring,1024,"<type>%x</type><code>%x</code><length>%u</length>\n",type,code,length); + snprintf(thestring, 1024, "<type>%x</type><code>%x</code><length>%u</length>\n", type, code, + length); ret = non_blocking_write(fd, thestring, strlen(thestring)); if (ret < 1) return; - if ((data!=NULL) && (length>0)) { - snprintf(thestring,1024,"<data encoding=\"base64\">\n"); + if ((data != NULL) && (length > 0)) { + snprintf(thestring, 1024, "<data encoding=\"base64\">\n"); ret = non_blocking_write(fd, thestring, strlen(thestring)); - if (ret < 1) // no reader + if (ret < 1) // no reader return; // here, we write the data in base64 form using our nice base64 encoder // but, we break it into lines of 76 output characters, except for the last one. @@ -954,37 +959,37 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) { char *remaining_data = data; size_t towrite_count; char outbuf[76]; - while ((remaining_count) && (ret>=0)) { - size_t towrite_count = remaining_count; - if (towrite_count>57) - towrite_count = 57; - size_t outbuf_size = 76; // size of output buffer on entry, length of result on exit - if (base64_encode_so(remaining_data, towrite_count, outbuf, &outbuf_size)==NULL) - debug(1,"Error encoding base64 data."); - //debug(1,"Remaining count: %d ret: %d, outbuf_size: %d.",remaining_count,ret,outbuf_size); - ret = non_blocking_write(fd,outbuf,outbuf_size); - if (ret<0) - return; - remaining_data+=towrite_count; - remaining_count-=towrite_count; + while ((remaining_count) && (ret >= 0)) { + size_t towrite_count = remaining_count; + if (towrite_count > 57) + towrite_count = 57; + size_t outbuf_size = 76; // size of output buffer on entry, length of result on exit + if (base64_encode_so(remaining_data, towrite_count, outbuf, &outbuf_size) == NULL) + debug(1, "Error encoding base64 data."); + // debug(1,"Remaining count: %d ret: %d, outbuf_size: %d.",remaining_count,ret,outbuf_size); + ret = non_blocking_write(fd, outbuf, outbuf_size); + if (ret < 0) + return; + remaining_data += towrite_count; + remaining_count -= towrite_count; // ret = write(fd,"\r\n",2); // if (ret<0) - // return; + // return; } - snprintf(thestring,1024,"</data>\n"); + snprintf(thestring, 1024, "</data>\n"); ret = non_blocking_write(fd, thestring, strlen(thestring)); - if (ret < 1) // no reader + if (ret < 1) // no reader return; } } -void* metadata_thread_function(void *ignore) { +void *metadata_thread_function(void *ignore) { metadata_create(); metadata_package pack; while (1) { pc_queue_get_item(&metadata_queue, &pack); if (config.metadata_enabled) - metadata_process(pack.type,pack.code,pack.data,pack.length); + metadata_process(pack.type, pack.code, pack.data, pack.length); if (pack.carrier) msg_free(pack.carrier); // release the message else if (pack.data) @@ -995,13 +1000,15 @@ void* metadata_thread_function(void *ignore) { void metadata_init(void) { // create a pc_queue for passing information to a threaded metadata handler - pc_queue_init(&metadata_queue,(char *)&metadata_queue_items,sizeof(metadata_package),metadata_queue_size); + pc_queue_init(&metadata_queue, (char *)&metadata_queue_items, sizeof(metadata_package), + metadata_queue_size); int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); if (ret) - debug(1,"Failed to create metadata thread!"); + debug(1, "Failed to create metadata thread!"); } -int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length, rtsp_message* carrier, int block) { +int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, + int block) { metadata_package pack; pack.type = type; pack.code = code; @@ -1010,102 +1017,104 @@ int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length, rtsp_m if (carrier) msg_retain(carrier); pack.carrier = carrier; - int rc = pc_queue_add_item(&metadata_queue,&pack,block); - if ((rc==EBUSY) && (carrier)) + int rc = pc_queue_add_item(&metadata_queue, &pack, block); + if ((rc == EBUSY) && (carrier)) msg_free(carrier); - if (rc==EBUSY) - warn("Metadata queue is busy, dropping message of type 0x%08X, code 0x%08X.",type,code); + if (rc == EBUSY) + warn("Metadata queue is busy, dropping message of type 0x%08X, code 0x%08X.", type, code); return rc; } -static void handle_set_parameter_metadata(rtsp_conn_info *conn, - rtsp_message *req, - rtsp_message *resp) { - char *cp = req->content; - int cl = req->contentlength; - - unsigned int off = 8; - - // inform the listener that a set of metadata is starting - // this doesn't include the cover art though... - - // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or NULL - // the rtsp_message is sent for 'core' messages, because it contains the data and must not be - // freed until the data has been read. So, it is passed to send_metadata to be retained, - // sent to the thread where metadata is processed and released (and probably freed). - - // The reading of the parameters is a bit complex - // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed in the thread handler when the parameter pointed to by the pointer and specified by the length is finished with - // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block and should be freed when the thread is finished with it. The length of the data in the block is given in length - // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done. - - send_metadata('ssnc','mdst',NULL,0,NULL,1); - - while (off < cl) { - // pick up the metadata tag as an unsigned longint - uint32_t itag = ntohl(*(uint32_t *)(cp+off)); - off += sizeof(uint32_t); - - // pick up the length of the data - uint32_t vl = ntohl(*(uint32_t *)(cp+off)); - off += sizeof(uint32_t); - - // pass the data over - if (vl==0) - send_metadata('core',itag,NULL,0,NULL,1); - else - send_metadata('core',itag,(char *)(cp+off),vl,req,1); - - // move on to the next item - off += vl; - } - - // inform the listener that a set of metadata is ending - send_metadata('ssnc','mden',NULL,0,NULL,1); +static void handle_set_parameter_metadata(rtsp_conn_info *conn, rtsp_message *req, + rtsp_message *resp) { + char *cp = req->content; + int cl = req->contentlength; + + unsigned int off = 8; + + // inform the listener that a set of metadata is starting + // this doesn't include the cover art though... + + // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or + // NULL + // the rtsp_message is sent for 'core' messages, because it contains the data and must not be + // freed until the data has been read. So, it is passed to send_metadata to be retained, + // sent to the thread where metadata is processed and released (and probably freed). + + // The reading of the parameters is a bit complex + // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed + // in the thread handler when the parameter pointed to by the pointer and specified by the length + // is finished with + // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block + // and should be freed when the thread is finished with it. The length of the data in the block is + // given in length + // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done. + + send_metadata('ssnc', 'mdst', NULL, 0, NULL, 1); + + while (off < cl) { + // pick up the metadata tag as an unsigned longint + uint32_t itag = ntohl(*(uint32_t *)(cp + off)); + off += sizeof(uint32_t); + + // pick up the length of the data + uint32_t vl = ntohl(*(uint32_t *)(cp + off)); + off += sizeof(uint32_t); + + // pass the data over + if (vl == 0) + send_metadata('core', itag, NULL, 0, NULL, 1); + else + send_metadata('core', itag, (char *)(cp + off), vl, req, 1); + + // move on to the next item + off += vl; + } + + // inform the listener that a set of metadata is ending + send_metadata('ssnc', 'mden', NULL, 0, NULL, 1); // send the user some shairport-originated metadata // send the name of the player, e.g. "Joe's iPhone" or "iTunes" - send_metadata('ssnc','sndr',strdup(sender_name),strlen(sender_name),NULL,1); + send_metadata('ssnc', 'sndr', strdup(sender_name), strlen(sender_name), NULL, 1); } #endif -static void handle_set_parameter(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { - //if (!req->contentlength) - // debug(1, "received empty SET_PARAMETER request."); +static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + // if (!req->contentlength) + // debug(1, "received empty SET_PARAMETER request."); - char *ct = msg_get_header(req, "Content-Type"); + char *ct = msg_get_header(req, "Content-Type"); - if (ct) { - debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); + if (ct) { + debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); #ifdef CONFIG_METADATA - if (!strncmp(ct, "application/x-dmap-tagged", 25)) { - debug(2, "received metadata tags in SET_PARAMETER request."); - handle_set_parameter_metadata(conn, req, resp); - } else if (!strncmp(ct, "image", 5)) { - // debug(1, "received image in SET_PARAMETER request."); - // note: the image/type tag isn't reliable, so it's not being sent - // -- best look at the first few bytes of the image - send_metadata('ssnc','PICT',req->content,req->contentlength,req,1); - } else -#endif - if (!strncmp(ct, "text/parameters", 15)) { - debug(2, "received parameters in SET_PARAMETER request."); - handle_set_parameter_parameter(conn, req, resp); - } else { - debug(1, "received unknown Content-Type \"%s\" in SET_PARAMETER request.", ct); - } + if (!strncmp(ct, "application/x-dmap-tagged", 25)) { + debug(2, "received metadata tags in SET_PARAMETER request."); + handle_set_parameter_metadata(conn, req, resp); + } else if (!strncmp(ct, "image", 5)) { + // debug(1, "received image in SET_PARAMETER request."); + // note: the image/type tag isn't reliable, so it's not being sent + // -- best look at the first few bytes of the image + send_metadata('ssnc', 'PICT', req->content, req->contentlength, req, 1); + } else +#endif + if (!strncmp(ct, "text/parameters", 15)) { + debug(2, "received parameters in SET_PARAMETER request."); + handle_set_parameter_parameter(conn, req, resp); } else { - debug(1, "missing Content-Type header in SET_PARAMETER request."); + debug(1, "received unknown Content-Type \"%s\" in SET_PARAMETER request.", ct); } + } else { + debug(1, "missing Content-Type header in SET_PARAMETER request."); + } - resp->respcode = 200; + resp->respcode = 200; } -static void handle_announce(rtsp_conn_info *conn, - rtsp_message *req, rtsp_message *resp) { +static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { // interrupt session if permitted - if ((config.allow_session_interruption==1) || (pthread_mutex_trylock(&play_lock) == 0)) { + if ((config.allow_session_interruption == 1) || (pthread_mutex_trylock(&play_lock) == 0)) { char *paesiv = NULL; char *prsaaeskey = NULL; char *pfmtp = NULL; @@ -1114,16 +1123,16 @@ static void handle_announce(rtsp_conn_info *conn, char *next; while (cp_left && cp) { next = nextline(cp, cp_left); - cp_left -= next-cp; + cp_left -= next - cp; if (!strncmp(cp, "a=fmtp:", 7)) - pfmtp = cp+7; + pfmtp = cp + 7; if (!strncmp(cp, "a=aesiv:", 8)) - paesiv = cp+8; + paesiv = cp + 8; if (!strncmp(cp, "a=rsaaeskey:", 12)) - prsaaeskey = cp+12; + prsaaeskey = cp + 12; cp = next; } @@ -1155,25 +1164,25 @@ static void handle_announce(rtsp_conn_info *conn, free(aeskey); int i; - for (i=0; i<sizeof(conn->stream.fmtp)/sizeof(conn->stream.fmtp[0]); i++) + for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++) conn->stream.fmtp[i] = atoi(strsep(&pfmtp, " \t")); - + char *hdr = msg_get_header(req, "X-Apple-Client-Name"); if (hdr) { - strncpy(sender_name,hdr,1024); - debug(1,"Play connection from \"%s\".",hdr); + strncpy(sender_name, hdr, 1024); + debug(1, "Play connection from \"%s\".", hdr); } else { hdr = msg_get_header(req, "User-Agent"); if (hdr) { - debug(1,"Play connection from \"%s\".",hdr); - strncpy(sender_name,hdr,1024); - } else - sender_name[0]=0; - } + debug(1, "Play connection from \"%s\".", hdr); + strncpy(sender_name, hdr, 1024); + } else + sender_name[0] = 0; + } resp->respcode = 200; } else { resp->respcode = 453; - debug(1,"Already playing."); + debug(1, "Already playing."); } out: @@ -1182,422 +1191,416 @@ out: } } - static struct method_handler { - char *method; - void (*handler)(rtsp_conn_info *conn, rtsp_message *req, - rtsp_message *resp); -} method_handlers[] = { - {"OPTIONS", handle_options}, - {"ANNOUNCE", handle_announce}, - {"FLUSH", handle_flush}, - {"TEARDOWN", handle_teardown}, - {"SETUP", handle_setup}, - {"GET_PARAMETER", handle_ignore}, - {"SET_PARAMETER", handle_set_parameter}, - {"RECORD", handle_record}, - {NULL, NULL} -}; + char *method; + void (*handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp); +} method_handlers[] = {{"OPTIONS", handle_options}, + {"ANNOUNCE", handle_announce}, + {"FLUSH", handle_flush}, + {"TEARDOWN", handle_teardown}, + {"SETUP", handle_setup}, + {"GET_PARAMETER", handle_ignore}, + {"SET_PARAMETER", handle_set_parameter}, + {"RECORD", handle_record}, + {NULL, NULL}}; static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) { - char *hdr = msg_get_header(req, "Apple-Challenge"); - if (!hdr) - return; + char *hdr = msg_get_header(req, "Apple-Challenge"); + if (!hdr) + return; - SOCKADDR fdsa; - socklen_t sa_len = sizeof(fdsa); - getsockname(fd, (struct sockaddr*)&fdsa, &sa_len); + SOCKADDR fdsa; + socklen_t sa_len = sizeof(fdsa); + getsockname(fd, (struct sockaddr *)&fdsa, &sa_len); - int chall_len; - uint8_t *chall = base64_dec(hdr, &chall_len); - uint8_t buf[48], *bp = buf; - int i; - memset(buf, 0, sizeof(buf)); + int chall_len; + uint8_t *chall = base64_dec(hdr, &chall_len); + uint8_t buf[48], *bp = buf; + int i; + memset(buf, 0, sizeof(buf)); - if (chall_len > 16) { - warn("oversized Apple-Challenge!"); - free(chall); - return; - } - memcpy(bp, chall, chall_len); + if (chall_len > 16) { + warn("oversized Apple-Challenge!"); free(chall); - bp += chall_len; + return; + } + memcpy(bp, chall, chall_len); + free(chall); + bp += chall_len; #ifdef AF_INET6 - if (fdsa.SAFAMILY == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)(&fdsa); - memcpy(bp, sa6->sin6_addr.s6_addr, 16); - bp += 16; - } else + if (fdsa.SAFAMILY == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)(&fdsa); + memcpy(bp, sa6->sin6_addr.s6_addr, 16); + bp += 16; + } else #endif - { - struct sockaddr_in *sa = (struct sockaddr_in*)(&fdsa); - memcpy(bp, &sa->sin_addr.s_addr, 4); - bp += 4; - } + { + struct sockaddr_in *sa = (struct sockaddr_in *)(&fdsa); + memcpy(bp, &sa->sin_addr.s_addr, 4); + bp += 4; + } - for (i=0; i<6; i++) - *bp++ = config.hw_addr[i]; + for (i = 0; i < 6; i++) + *bp++ = config.hw_addr[i]; - int buflen, resplen; - buflen = bp-buf; - if (buflen < 0x20) - buflen = 0x20; + int buflen, resplen; + buflen = bp - buf; + if (buflen < 0x20) + buflen = 0x20; - uint8_t *challresp = rsa_apply(buf, buflen, &resplen, RSA_MODE_AUTH); - char *encoded = base64_enc(challresp, resplen); + uint8_t *challresp = rsa_apply(buf, buflen, &resplen, RSA_MODE_AUTH); + char *encoded = base64_enc(challresp, resplen); - // strip the padding. - char *padding = strchr(encoded, '='); - if (padding) - *padding = 0; + // strip the padding. + char *padding = strchr(encoded, '='); + if (padding) + *padding = 0; - msg_add_header(resp, "Apple-Response", encoded); - free(challresp); - free(encoded); + msg_add_header(resp, "Apple-Response", encoded); + free(challresp); + free(encoded); } static char *make_nonce(void) { - uint8_t random[8]; - int fd = open("/dev/random", O_RDONLY); - if (fd < 0) - die("could not open /dev/random!"); - int ignore = read(fd, random, sizeof(random)); - close(fd); - return base64_enc(random, 8); + uint8_t random[8]; + int fd = open("/dev/random", O_RDONLY); + if (fd < 0) + die("could not open /dev/random!"); + int ignore = read(fd, random, sizeof(random)); + close(fd); + return base64_enc(random, 8); } static int rtsp_auth(char **nonce, rtsp_message *req, rtsp_message *resp) { - if (!config.password) - return 0; - if (!*nonce) { - *nonce = make_nonce(); - goto authenticate; - } + if (!config.password) + return 0; + if (!*nonce) { + *nonce = make_nonce(); + goto authenticate; + } + + char *hdr = msg_get_header(req, "Authorization"); + if (!hdr || strncmp(hdr, "Digest ", 7)) + goto authenticate; + + char *realm = strstr(hdr, "realm=\""); + char *username = strstr(hdr, "username=\""); + char *response = strstr(hdr, "response=\""); + char *uri = strstr(hdr, "uri=\""); + + if (!realm || !username || !response || !uri) + goto authenticate; + + char *quote; + realm = strchr(realm, '"') + 1; + if (!(quote = strchr(realm, '"'))) + goto authenticate; + *quote = 0; + username = strchr(username, '"') + 1; + if (!(quote = strchr(username, '"'))) + goto authenticate; + *quote = 0; + response = strchr(response, '"') + 1; + if (!(quote = strchr(response, '"'))) + goto authenticate; + *quote = 0; + uri = strchr(uri, '"') + 1; + if (!(quote = strchr(uri, '"'))) + goto authenticate; + *quote = 0; + + uint8_t digest_urp[16], digest_mu[16], digest_total[16]; - char *hdr = msg_get_header(req, "Authorization"); - if (!hdr || strncmp(hdr, "Digest ", 7)) - goto authenticate; - - char *realm = strstr(hdr, "realm=\""); - char *username = strstr(hdr, "username=\""); - char *response = strstr(hdr, "response=\""); - char *uri = strstr(hdr, "uri=\""); - - if (!realm || !username || !response || !uri) - goto authenticate; - - char *quote; - realm = strchr(realm, '"') + 1; - if (!(quote = strchr(realm, '"'))) - goto authenticate; - *quote = 0; - username = strchr(username, '"') + 1; - if (!(quote = strchr(username, '"'))) - goto authenticate; - *quote = 0; - response = strchr(response, '"') + 1; - if (!(quote = strchr(response, '"'))) - goto authenticate; - *quote = 0; - uri = strchr(uri, '"') + 1; - if (!(quote = strchr(uri, '"'))) - goto authenticate; - *quote = 0; - - uint8_t digest_urp[16], digest_mu[16], digest_total[16]; - #ifdef HAVE_LIBSSL - MD5_CTX ctx; - - MD5_Init(&ctx); - MD5_Update(&ctx, username, strlen(username)); - MD5_Update(&ctx, ":", 1); - MD5_Update(&ctx, realm, strlen(realm)); - MD5_Update(&ctx, ":", 1); - MD5_Update(&ctx, config.password, strlen(config.password)); - MD5_Final(digest_urp, &ctx); - MD5_Init(&ctx); - MD5_Update(&ctx, req->method, strlen(req->method)); - MD5_Update(&ctx, ":", 1); - MD5_Update(&ctx, uri, strlen(uri)); - MD5_Final(digest_mu, &ctx); + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, username, strlen(username)); + MD5_Update(&ctx, ":", 1); + MD5_Update(&ctx, realm, strlen(realm)); + MD5_Update(&ctx, ":", 1); + MD5_Update(&ctx, config.password, strlen(config.password)); + MD5_Final(digest_urp, &ctx); + MD5_Init(&ctx); + MD5_Update(&ctx, req->method, strlen(req->method)); + MD5_Update(&ctx, ":", 1); + MD5_Update(&ctx, uri, strlen(uri)); + MD5_Final(digest_mu, &ctx); #endif - #ifdef HAVE_LIBPOLARSSL - md5_context tctx; - md5_starts(&tctx); - md5_update(&tctx, (const unsigned char *)username, strlen(username)); - md5_update(&tctx, (unsigned char *) ":", 1); - md5_update(&tctx, (const unsigned char *)realm, strlen(realm)); - md5_update(&tctx, (unsigned char *) ":", 1); - md5_update(&tctx, (const unsigned char *)config.password, strlen(config.password)); - md5_finish(&tctx,digest_urp); - md5_starts(&tctx); - md5_update(&tctx, (const unsigned char *)req->method, strlen(req->method)); - md5_update(&tctx, (unsigned char *) ":", 1); - md5_update(&tctx, (const unsigned char *)uri, strlen(uri)); - md5_finish(&tctx,digest_mu); + md5_context tctx; + md5_starts(&tctx); + md5_update(&tctx, (const unsigned char *)username, strlen(username)); + md5_update(&tctx, (unsigned char *)":", 1); + md5_update(&tctx, (const unsigned char *)realm, strlen(realm)); + md5_update(&tctx, (unsigned char *)":", 1); + md5_update(&tctx, (const unsigned char *)config.password, strlen(config.password)); + md5_finish(&tctx, digest_urp); + md5_starts(&tctx); + md5_update(&tctx, (const unsigned char *)req->method, strlen(req->method)); + md5_update(&tctx, (unsigned char *)":", 1); + md5_update(&tctx, (const unsigned char *)uri, strlen(uri)); + md5_finish(&tctx, digest_mu); #endif - - int i; - unsigned char buf[33]; - for (i=0; i<16; i++) - sprintf((char *)buf + 2*i, "%02X", digest_urp[i]); - + + int i; + unsigned char buf[33]; + for (i = 0; i < 16; i++) + sprintf((char *)buf + 2 * i, "%02X", digest_urp[i]); + #ifdef HAVE_LIBSSL - MD5_Init(&ctx); - MD5_Update(&ctx, buf, 32); - MD5_Update(&ctx, ":", 1); - MD5_Update(&ctx, *nonce, strlen(*nonce)); - MD5_Update(&ctx, ":", 1); - for (i=0; i<16; i++) - sprintf(buf + 2*i, "%02X", digest_mu[i]); - MD5_Update(&ctx, buf, 32); - MD5_Final(digest_total, &ctx); - #endif - - + MD5_Init(&ctx); + MD5_Update(&ctx, buf, 32); + MD5_Update(&ctx, ":", 1); + MD5_Update(&ctx, *nonce, strlen(*nonce)); + MD5_Update(&ctx, ":", 1); + for (i = 0; i < 16; i++) + sprintf(buf + 2 * i, "%02X", digest_mu[i]); + MD5_Update(&ctx, buf, 32); + MD5_Final(digest_total, &ctx); +#endif + #ifdef HAVE_LIBPOLARSSL - md5_starts(&tctx); - md5_update(&tctx, buf, 32); - md5_update(&tctx, (unsigned char *) ":", 1); - md5_update(&tctx, (const unsigned char *)*nonce, strlen(*nonce)); - md5_update(&tctx, (unsigned char *) ":", 1); - for (i=0; i<16; i++) - sprintf((char *)buf + 2*i,"%02X", digest_mu[i]); - md5_update(&tctx, buf, 32); - md5_finish(&tctx,digest_total); + md5_starts(&tctx); + md5_update(&tctx, buf, 32); + md5_update(&tctx, (unsigned char *)":", 1); + md5_update(&tctx, (const unsigned char *)*nonce, strlen(*nonce)); + md5_update(&tctx, (unsigned char *)":", 1); + for (i = 0; i < 16; i++) + sprintf((char *)buf + 2 * i, "%02X", digest_mu[i]); + md5_update(&tctx, buf, 32); + md5_finish(&tctx, digest_total); #endif - for (i=0; i<16; i++) - sprintf((char *)buf + 2*i,"%02X", digest_total[i]); + for (i = 0; i < 16; i++) + sprintf((char *)buf + 2 * i, "%02X", digest_total[i]); - if (!strcmp(response, (const char *)buf)) - return 0; - warn("auth failed"); + if (!strcmp(response, (const char *)buf)) + return 0; + warn("auth failed"); authenticate: - resp->respcode = 401; - int hdrlen = strlen(*nonce) + 40; - char *authhdr = malloc(hdrlen); - snprintf(authhdr, hdrlen, "Digest realm=\"taco\", nonce=\"%s\"", *nonce); - msg_add_header(resp, "WWW-Authenticate", authhdr); - free(authhdr); - return 1; + resp->respcode = 401; + int hdrlen = strlen(*nonce) + 40; + char *authhdr = malloc(hdrlen); + snprintf(authhdr, hdrlen, "Digest realm=\"taco\", nonce=\"%s\"", *nonce); + msg_add_header(resp, "WWW-Authenticate", authhdr); + free(authhdr); + return 1; } static void *rtsp_conversation_thread_func(void *pconn) { - // SIGUSR1 is used to interrupt this thread if blocked for read - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - rtsp_conn_info *conn = pconn; - - rtsp_message *req, *resp; - char *hdr, *auth_nonce = NULL; - - enum rtsp_read_request_response reply; - - do { - reply=rtsp_read_request(conn->fd,&req); - if (reply==rtsp_read_request_response_ok) { - resp = msg_init(); - resp->respcode = 400; - - apple_challenge(conn->fd, req, resp); - hdr = msg_get_header(req, "CSeq"); - if (hdr) - msg_add_header(resp, "CSeq", hdr); - msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog"); - - if (rtsp_auth(&auth_nonce, req, resp)) - goto respond; - - struct method_handler *mh; - for (mh=method_handlers; mh->method; mh++) { - if (!strcmp(mh->method, req->method)) { - // debug(1,"RTSP Packet received of type \"%s\":",mh->method), - // msg_print_debug_headers(req); - mh->handler(conn, req, resp); - // debug(1,"RTSP Response:"); - // msg_print_debug_headers(resp); - break; - } - } + // SIGUSR1 is used to interrupt this thread if blocked for read + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + rtsp_conn_info *conn = pconn; -respond: - msg_write_response(conn->fd, resp); - msg_free(req); - msg_free(resp); - } else { - if (reply!=rtsp_read_request_response_shutdown_requested) - debug(1,"rtsp_read_request error %d, packet ignored.",(int)reply); + rtsp_message *req, *resp; + char *hdr, *auth_nonce = NULL; + + enum rtsp_read_request_response reply; + + do { + reply = rtsp_read_request(conn->fd, &req); + if (reply == rtsp_read_request_response_ok) { + resp = msg_init(); + resp->respcode = 400; + + apple_challenge(conn->fd, req, resp); + hdr = msg_get_header(req, "CSeq"); + if (hdr) + msg_add_header(resp, "CSeq", hdr); + msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog"); + + if (rtsp_auth(&auth_nonce, req, resp)) + goto respond; + + struct method_handler *mh; + for (mh = method_handlers; mh->method; mh++) { + if (!strcmp(mh->method, req->method)) { + // debug(1,"RTSP Packet received of type \"%s\":",mh->method), + // msg_print_debug_headers(req); + mh->handler(conn, req, resp); + // debug(1,"RTSP Response:"); + // msg_print_debug_headers(resp); + break; + } } - } while (reply!=rtsp_read_request_response_shutdown_requested); - - debug(1, "closing RTSP connection."); - if (conn->fd > 0) - close(conn->fd); - if (rtsp_playing()) { - rtp_shutdown(); - player_stop(); - pthread_mutex_unlock(&play_lock); - please_shutdown = 0; - pthread_mutex_unlock(&playing_mutex); + + respond: + msg_write_response(conn->fd, resp); + msg_free(req); + msg_free(resp); + } else { + if (reply != rtsp_read_request_response_shutdown_requested) + debug(1, "rtsp_read_request error %d, packet ignored.", (int)reply); } - if (auth_nonce) - free(auth_nonce); - conn->running = 0; - debug(2, "terminating RTSP thread."); - return NULL; + } while (reply != rtsp_read_request_response_shutdown_requested); + + debug(1, "closing RTSP connection."); + if (conn->fd > 0) + close(conn->fd); + if (rtsp_playing()) { + rtp_shutdown(); + player_stop(); + pthread_mutex_unlock(&play_lock); + please_shutdown = 0; + pthread_mutex_unlock(&playing_mutex); + } + if (auth_nonce) + free(auth_nonce); + conn->running = 0; + debug(2, "terminating RTSP thread."); + return NULL; } // this function is not thread safe. -static const char* format_address(struct sockaddr *fsa) { - static char string[INETx_ADDRSTRLEN]; - void *addr; +static const char *format_address(struct sockaddr *fsa) { + static char string[INETx_ADDRSTRLEN]; + void *addr; #ifdef AF_INET6 - if (fsa->sa_family == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)(fsa); - addr = &(sa6->sin6_addr); - } else + if (fsa->sa_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)(fsa); + addr = &(sa6->sin6_addr); + } else #endif - { - struct sockaddr_in *sa = (struct sockaddr_in*)(fsa); - addr = &(sa->sin_addr); - } - return inet_ntop(fsa->sa_family, addr, string, sizeof(string)); + { + struct sockaddr_in *sa = (struct sockaddr_in *)(fsa); + addr = &(sa->sin_addr); + } + return inet_ntop(fsa->sa_family, addr, string, sizeof(string)); } void rtsp_listen_loop(void) { - struct addrinfo hints, *info, *p; - char portstr[6]; - int *sockfd = NULL; - int nsock = 0; - int i, ret; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - snprintf(portstr, 6, "%d", config.port); - - // debug(1,"listen socket port request is \"%s\".",portstr); - - ret = getaddrinfo(NULL, portstr, &hints, &info); - if (ret) { - die("getaddrinfo failed: %s", gai_strerror(ret)); - } + struct addrinfo hints, *info, *p; + char portstr[6]; + int *sockfd = NULL; + int nsock = 0; + int i, ret; - for (p=info; p; p=p->ai_next) { - int fd = socket(p->ai_family, p->ai_socktype, IPPROTO_TCP); - int yes = 1; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; - // Handle socket open failures if protocol unavailable (or IPV6 not handled) - if (fd == -1) { - // debug(1, "Failed to get socket: fam=%d, %s\n", p->ai_family, strerror(errno)); - continue; - } + snprintf(portstr, 6, "%d", config.port); + + // debug(1,"listen socket port request is \"%s\".",portstr); + + ret = getaddrinfo(NULL, portstr, &hints, &info); + if (ret) { + die("getaddrinfo failed: %s", gai_strerror(ret)); + } - ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + for (p = info; p; p = p->ai_next) { + int fd = socket(p->ai_family, p->ai_socktype, IPPROTO_TCP); + int yes = 1; + + // Handle socket open failures if protocol unavailable (or IPV6 not handled) + if (fd == -1) { + // debug(1, "Failed to get socket: fam=%d, %s\n", p->ai_family, strerror(errno)); + continue; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); #ifdef IPV6_V6ONLY - // some systems don't support v4 access on v6 sockets, but some do. - // since we need to account for two sockets we might as well - // always. - if (p->ai_family == AF_INET6) { - ret |= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)); - } + // some systems don't support v4 access on v6 sockets, but some do. + // since we need to account for two sockets we might as well + // always. + if (p->ai_family == AF_INET6) { + ret |= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)); + } #endif - if (!ret) - ret = bind(fd, p->ai_addr, p->ai_addrlen); - - // one of the address families will fail on some systems that - // report its availability. do not complain. - if (ret) { - debug(1, "Failed to bind to address %s.", format_address(p->ai_addr)); - continue; - } + if (!ret) + ret = bind(fd, p->ai_addr, p->ai_addrlen); - listen(fd, 5); - nsock++; - sockfd = realloc(sockfd, nsock*sizeof(int)); - sockfd[nsock-1] = fd; + // one of the address families will fail on some systems that + // report its availability. do not complain. + if (ret) { + debug(1, "Failed to bind to address %s.", format_address(p->ai_addr)); + continue; } - freeaddrinfo(info); + listen(fd, 5); + nsock++; + sockfd = realloc(sockfd, nsock * sizeof(int)); + sockfd[nsock - 1] = fd; + } - if (!nsock) - die("could not bind any listen sockets!"); + freeaddrinfo(info); - int maxfd = -1; - fd_set fds; - FD_ZERO(&fds); - for (i=0; i<nsock; i++) { - if (sockfd[i] > maxfd) - maxfd = sockfd[i]; - } + if (!nsock) + die("could not bind any listen sockets!"); - mdns_register(); + int maxfd = -1; + fd_set fds; + FD_ZERO(&fds); + for (i = 0; i < nsock; i++) { + if (sockfd[i] > maxfd) + maxfd = sockfd[i]; + } - // printf("Listening for connections."); - // shairport_startup_complete(); + mdns_register(); - int acceptfd; - struct timeval tv; - while (1) { - tv.tv_sec = 300; - tv.tv_usec = 0; + // printf("Listening for connections."); + // shairport_startup_complete(); - for (i=0; i<nsock; i++) - FD_SET(sockfd[i], &fds); + int acceptfd; + struct timeval tv; + while (1) { + tv.tv_sec = 300; + tv.tv_usec = 0; - ret = select(maxfd+1, &fds, 0, 0, &tv); - if (ret<0) { - if (errno==EINTR) - continue; - break; - } + for (i = 0; i < nsock; i++) + FD_SET(sockfd[i], &fds); - cleanup_threads(); + ret = select(maxfd + 1, &fds, 0, 0, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } - acceptfd = -1; - for (i=0; i<nsock; i++) { - if (FD_ISSET(sockfd[i], &fds)) { - acceptfd = sockfd[i]; - break; - } - } - if (acceptfd < 0) // timeout - continue; - - rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info)); - memset(conn, 0, sizeof(rtsp_conn_info)); - socklen_t slen = sizeof(conn->remote); - - debug(1, "new RTSP connection."); - conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen); - if (conn->fd < 0) { - perror("failed to accept connection"); - free(conn); - } else { - pthread_t rtsp_conversation_thread; - ret = pthread_create(&rtsp_conversation_thread, NULL, rtsp_conversation_thread_func, conn); - if (ret) - die("Failed to create RTSP receiver thread!"); - - conn->thread = rtsp_conversation_thread; - conn->running = 1; - track_thread(conn); - } + cleanup_threads(); + + acceptfd = -1; + for (i = 0; i < nsock; i++) { + if (FD_ISSET(sockfd[i], &fds)) { + acceptfd = sockfd[i]; + break; + } } - perror("select"); - die("fell out of the RTSP select loop"); + if (acceptfd < 0) // timeout + continue; + + rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info)); + memset(conn, 0, sizeof(rtsp_conn_info)); + socklen_t slen = sizeof(conn->remote); + + debug(1, "new RTSP connection."); + conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen); + if (conn->fd < 0) { + perror("failed to accept connection"); + free(conn); + } else { + pthread_t rtsp_conversation_thread; + ret = pthread_create(&rtsp_conversation_thread, NULL, rtsp_conversation_thread_func, conn); + if (ret) + die("Failed to create RTSP receiver thread!"); + + conn->thread = rtsp_conversation_thread; + conn->running = 1; + track_thread(conn); + } + } + perror("select"); + die("fell out of the RTSP select loop"); } @@ -11,6 +11,6 @@ void rtsp_request_shutdown_stream(void); // e.g. it it's malloced, to free it, etc. // nothing is done automatically -int send_ssnc_metadata(uint32_t code,char *data,uint32_t length, int block); +int send_ssnc_metadata(uint32_t code, char *data, uint32_t length, int block); #endif // _RTSP_H diff --git a/shairport.c b/shairport.c index cf8ce06..5ab2638 100644 --- a/shairport.c +++ b/shairport.c @@ -60,518 +60,536 @@ #include <libdaemon/dpid.h> #include <libdaemon/dexec.h> - static int shutting_down = 0; -static char* appName = NULL; +static char *appName = NULL; void shairport_shutdown() { - if (shutting_down) - return; - shutting_down = 1; - mdns_unregister(); - rtsp_shutdown_stream(); - if (config.output) - config.output->deinit(); + if (shutting_down) + return; + shutting_down = 1; + mdns_unregister(); + rtsp_shutdown_stream(); + if (config.output) + config.output->deinit(); } -static void sig_ignore(int foo, siginfo_t *bar, void *baz) { -} +static void sig_ignore(int foo, siginfo_t *bar, void *baz) {} static void sig_shutdown(int foo, siginfo_t *bar, void *baz) { - debug(1, "shutdown requested..."); - shairport_shutdown(); - daemon_log(LOG_NOTICE, "exit..."); - daemon_retval_send(255); - daemon_pid_file_remove(); - exit(0); + debug(1, "shutdown requested..."); + shairport_shutdown(); + daemon_log(LOG_NOTICE, "exit..."); + daemon_retval_send(255); + daemon_pid_file_remove(); + exit(0); } static void sig_child(int foo, siginfo_t *bar, void *baz) { - pid_t pid; - while ((pid = waitpid((pid_t)-1, 0, WNOHANG)) > 0) { - if (pid == mdns_pid && !shutting_down) { - die("MDNS child process died unexpectedly!"); - } - } + pid_t pid; + while ((pid = waitpid((pid_t)-1, 0, WNOHANG)) > 0) { + if (pid == mdns_pid && !shutting_down) { + die("MDNS child process died unexpectedly!"); + } + } } static void sig_disconnect_audio_output(int foo, siginfo_t *bar, void *baz) { - debug(1,"disconnect audio output requested."); - set_requested_connection_state_to_output(0); + debug(1, "disconnect audio output requested."); + set_requested_connection_state_to_output(0); } static void sig_connect_audio_output(int foo, siginfo_t *bar, void *baz) { - debug(1,"connect audio output requested."); - set_requested_connection_state_to_output(1); + debug(1, "connect audio output requested."); + set_requested_connection_state_to_output(1); } void print_version(void) { char version_string[200]; - strcpy(version_string,PACKAGE_VERSION); + strcpy(version_string, PACKAGE_VERSION); #ifdef HAVE_LIBPOLARSSL - strcat(version_string,"-polarssl"); + strcat(version_string, "-polarssl"); #endif #ifdef HAVE_LIBSSL - strcat(version_string,"-openssl"); + strcat(version_string, "-openssl"); #endif #ifdef CONFIG_TINYSVCMDNS - strcat(version_string,"-tinysvcmdns"); + strcat(version_string, "-tinysvcmdns"); #endif #ifdef CONFIG_AVAHI - strcat(version_string,"-Avahi"); + strcat(version_string, "-Avahi"); #endif #ifdef CONFIG_DNS_SD - strcat(version_string,"-dns_sd"); + strcat(version_string, "-dns_sd"); #endif #ifdef CONFIG_ALSA - strcat(version_string,"-ALSA"); + strcat(version_string, "-ALSA"); #endif #ifdef CONFIG_SNDIO - strcat(version_string,"-sndio"); + strcat(version_string, "-sndio"); #endif #ifdef CONFIG_AO - strcat(version_string,"-ao"); + strcat(version_string, "-ao"); #endif #ifdef CONFIG_PULSE - strcat(version_string,"-pulse"); + strcat(version_string, "-pulse"); #endif #ifdef HAVE_LIBSOXR - strcat(version_string,"-soxr"); + strcat(version_string, "-soxr"); #endif #ifdef CONFIG_METADATA - strcat(version_string,"-metadata"); + strcat(version_string, "-metadata"); #endif #ifdef SUPPORT_CONFIG_FILES - strcat(version_string,"-configfile"); + strcat(version_string, "-configfile"); #endif - printf("%s\n",version_string); + printf("%s\n", version_string); } void usage(char *progname) { - printf("Usage: %s [options...]\n", progname); - printf(" or: %s [options...] -- [audio output-specific options]\n", progname); - printf("\n"); - printf("Options:\n"); - printf(" -h, --help show this help\n"); - printf(" -d, --daemon daemonise.\n"); - printf(" -V, --version show version information\n"); - printf(" -k, --kill kill the existing shairport daemon.\n"); - printf(" -D, --disconnectFromOutput disconnect immediately from the output device.\n"); - printf(" -R, --reconnectToOutput reconnect to the output device.\n"); - printf(" -c, --configfile=FILE read configuration settings from FILE. Default is /etc/shairport-sync.conf.\n"); + printf("Usage: %s [options...]\n", progname); + printf(" or: %s [options...] -- [audio output-specific options]\n", progname); + printf("\n"); + printf("Options:\n"); + printf(" -h, --help show this help\n"); + printf(" -d, --daemon daemonise.\n"); + printf(" -V, --version show version information\n"); + printf(" -k, --kill kill the existing shairport daemon.\n"); + printf(" -D, --disconnectFromOutput disconnect immediately from the output device.\n"); + printf(" -R, --reconnectToOutput reconnect to the output device.\n"); + printf(" -c, --configfile=FILE read configuration settings from FILE. Default is " + "/etc/shairport-sync.conf.\n"); #ifdef COMMAND_LINE_ARGUMENT_SUPPORT - printf(" -v, --verbose -v print debug information; -vv more; -vvv lots\n"); - printf(" -p, --port=PORT set RTSP listening port\n"); - printf(" -a, --name=NAME set advertised name\n"); - printf(" -A, --AirPlayLatency=FRAMES set the latency for audio sent from an AirPlay device.\n"); - printf(" The default value is %u frames.\n", config.AirPlayLatency); - printf(" -i, --iTunesLatency=FRAMES set the latency for audio sent from iTunes 10 or later.\n"); - printf(" The default value is %u frames.\n", config.iTunesLatency); - printf(" -L, --latency=FRAMES set the latency for audio sent from an unknown device\n"); - printf(" or from an old version of iTunes. Default is %d frames.\n",config.latency); - printf(" --forkedDaapdLatency=FRAMES set the latency for audio sent from forked-daapd.\n"); - printf(" -S, --stuffing=MODE set how to adjust current latency to match desired latency \n"); - printf(" \"basic\" (default) inserts or deletes audio frames from packet frames with low processor overhead.\n"); - printf(" \"soxr\" uses libsoxr to minimally resample packet frames -- moderate processor overhead.\n"); - printf(" \"soxr\" option only available if built with soxr support.\n"); - printf(" -B, --on-start=PROGRAM run PROGRAM when playback is about to begin.\n"); - printf(" -E, --on-stop=PROGRAM run PROGRAM when playback has ended.\n"); - printf(" For -B and -E options, specify the full path to the program, e.g. /usr/bin/logger.\n"); - printf(" Executable scripts work, but must have #!/bin/sh (or whatever) in the headline.\n"); - printf(" -w, --wait-cmd wait until the -B or -E programs finish before continuing\n"); - printf(" -o, --output=BACKEND select audio output method\n"); - printf(" -m, --mdns=BACKEND force the use of BACKEND to advertize the service\n"); - printf(" if no mdns provider is specified,\n"); - printf(" shairport tries them all until one works.\n"); - printf(" -r, --resync=THRESHOLD resync if error exceeds this number of frames. Set to 0 to stop resyncing.\n"); - printf(" -t, --timeout=SECONDS go back to idle mode from play mode after a break in communications of this many seconds (default 120). Set to 0 never to exit play mode.\n"); - printf(" --statistics print some interesting statistics -- output to the logfile if running as a daemon.\n"); - printf(" --tolerance=TOLERANCE allow a synchronization error of TOLERANCE frames (default 88) before trying to correct it.\n"); - printf(" --password=PASSWORD require PASSWORD to connect. Default is not to require a password.\n"); + printf(" -v, --verbose -v print debug information; -vv more; -vvv lots\n"); + printf(" -p, --port=PORT set RTSP listening port\n"); + printf(" -a, --name=NAME set advertised name\n"); + printf( + " -A, --AirPlayLatency=FRAMES set the latency for audio sent from an AirPlay device.\n"); + printf(" The default value is %u frames.\n", config.AirPlayLatency); + printf( + " -i, --iTunesLatency=FRAMES set the latency for audio sent from iTunes 10 or later.\n"); + printf(" The default value is %u frames.\n", config.iTunesLatency); + printf(" -L, --latency=FRAMES set the latency for audio sent from an unknown device\n"); + printf(" or from an old version of iTunes. Default is %d frames.\n", + config.latency); + printf(" --forkedDaapdLatency=FRAMES set the latency for audio sent from forked-daapd.\n"); + printf(" -S, --stuffing=MODE set how to adjust current latency to match desired latency \n"); + printf(" \"basic\" (default) inserts or deletes audio frames from " + "packet frames with low processor overhead.\n"); + printf(" \"soxr\" uses libsoxr to minimally resample packet frames -- " + "moderate processor overhead.\n"); + printf( + " \"soxr\" option only available if built with soxr support.\n"); + printf(" -B, --on-start=PROGRAM run PROGRAM when playback is about to begin.\n"); + printf(" -E, --on-stop=PROGRAM run PROGRAM when playback has ended.\n"); + printf(" For -B and -E options, specify the full path to the program, " + "e.g. /usr/bin/logger.\n"); + printf(" Executable scripts work, but must have #!/bin/sh (or " + "whatever) in the headline.\n"); + printf(" -w, --wait-cmd wait until the -B or -E programs finish before continuing\n"); + printf(" -o, --output=BACKEND select audio output method\n"); + printf(" -m, --mdns=BACKEND force the use of BACKEND to advertize the service\n"); + printf(" if no mdns provider is specified,\n"); + printf(" shairport tries them all until one works.\n"); + printf(" -r, --resync=THRESHOLD resync if error exceeds this number of frames. Set to 0 to " + "stop resyncing.\n"); + printf(" -t, --timeout=SECONDS go back to idle mode from play mode after a break in " + "communications of this many seconds (default 120). Set to 0 never to exit play mode.\n"); + printf(" --statistics print some interesting statistics -- output to the logfile " + "if running as a daemon.\n"); + printf(" --tolerance=TOLERANCE allow a synchronization error of TOLERANCE frames (default " + "88) before trying to correct it.\n"); + printf(" --password=PASSWORD require PASSWORD to connect. Default is not to require a " + "password.\n"); #ifdef CONFIG_METADATA - printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. --metadata-pipename=/tmp/shairport-sync-metadata.\n"); - printf(" --get-coverart send cover art through the metadata pipe.\n"); + printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. " + "--metadata-pipename=/tmp/shairport-sync-metadata.\n"); + printf(" --get-coverart send cover art through the metadata pipe.\n"); #endif #endif #ifdef SUPPORT_CONFIG_FILES - printf("\nGeneral options can be configured in /etc/%s.conf.\n",appName); + printf("\nGeneral options can be configured in /etc/%s.conf.\n", appName); #endif - printf("\n"); - mdns_ls_backends(); - printf("\n"); - audio_ls_outputs(); + printf("\n"); + mdns_ls_backends(); + printf("\n"); + audio_ls_outputs(); } int parse_options(int argc, char **argv) { - char *stuffing = NULL; /* used for picking up the stuffing option */ - - - - - signed char c; /* used for argument parsing */ - int i = 0; /* used for tracking options */ - poptContext optCon; /* context for parsing command-line options */ + char *stuffing = NULL; /* used for picking up the stuffing option */ + + signed char c; /* used for argument parsing */ + int i = 0; /* used for tracking options */ + poptContext optCon; /* context for parsing command-line options */ struct poptOption optionsTable[] = { - { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', NULL }, - { "disconnectFromOutput", 'D', POPT_ARG_NONE, NULL, 0, NULL }, - { "reconnectToOutput", 'R', POPT_ARG_NONE, NULL, 0, NULL }, - { "kill", 'k', POPT_ARG_NONE, NULL, 0, NULL }, - { "daemon", 'd', POPT_ARG_NONE, &config.daemonise, 0, NULL }, + {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', NULL}, + {"disconnectFromOutput", 'D', POPT_ARG_NONE, NULL, 0, NULL}, + {"reconnectToOutput", 'R', POPT_ARG_NONE, NULL, 0, NULL}, + {"kill", 'k', POPT_ARG_NONE, NULL, 0, NULL}, + {"daemon", 'd', POPT_ARG_NONE, &config.daemonise, 0, NULL}, #ifdef SUPPORT_CONFIG_FILES - { "configfile", 'c', POPT_ARG_STRING, &config.configfile, 0, NULL }, + {"configfile", 'c', POPT_ARG_STRING, &config.configfile, 0, NULL}, #endif #ifdef COMMAND_LINE_ARGUMENT_SUPPORT - { "statistics", 0, POPT_ARG_NONE, &config.statistics_requested, 0, NULL}, - { "version", 'V', POPT_ARG_NONE, NULL, 0, NULL}, - { "port", 'p', POPT_ARG_INT, &config.port, 0, NULL } , - { "name", 'a', POPT_ARG_STRING, &config.apname, 0, NULL } , - { "output", 'o', POPT_ARG_STRING, &config.output_name, 0, NULL } , - { "on-start", 'B', POPT_ARG_STRING, &config.cmd_start, 0, NULL } , - { "on-stop", 'E', POPT_ARG_STRING, &config.cmd_stop, 0, NULL } , - { "wait-cmd", 'w', POPT_ARG_NONE, &config.cmd_blocking, 0, NULL } , - { "mdns", 'm', POPT_ARG_STRING, &config.mdns_name, 0, NULL } , - { "latency", 'L', POPT_ARG_INT, &config.userSuppliedLatency, 0, NULL } , - { "AirPlayLatency", 'A', POPT_ARG_INT, &config.AirPlayLatency, 0, NULL } , - { "iTunesLatency", 'i', POPT_ARG_INT, &config.iTunesLatency, 0, NULL } , - { "forkedDaapdLatency", 0, POPT_ARG_INT, &config.ForkedDaapdLatency, 0, NULL } , - { "stuffing", 'S', POPT_ARG_STRING, &stuffing, 'S', NULL } , - { "resync", 'r', POPT_ARG_INT, &config.resyncthreshold, 0, NULL } , - { "timeout", 't', POPT_ARG_INT, &config.timeout, 't', NULL } , - { "password", 0, POPT_ARG_STRING, &config.password, 0, NULL } , - { "tolerance", 0, POPT_ARG_INT, &config.tolerance, 0, NULL } , + {"statistics", 0, POPT_ARG_NONE, &config.statistics_requested, 0, NULL}, + {"version", 'V', POPT_ARG_NONE, NULL, 0, NULL}, + {"port", 'p', POPT_ARG_INT, &config.port, 0, NULL}, + {"name", 'a', POPT_ARG_STRING, &config.apname, 0, NULL}, + {"output", 'o', POPT_ARG_STRING, &config.output_name, 0, NULL}, + {"on-start", 'B', POPT_ARG_STRING, &config.cmd_start, 0, NULL}, + {"on-stop", 'E', POPT_ARG_STRING, &config.cmd_stop, 0, NULL}, + {"wait-cmd", 'w', POPT_ARG_NONE, &config.cmd_blocking, 0, NULL}, + {"mdns", 'm', POPT_ARG_STRING, &config.mdns_name, 0, NULL}, + {"latency", 'L', POPT_ARG_INT, &config.userSuppliedLatency, 0, NULL}, + {"AirPlayLatency", 'A', POPT_ARG_INT, &config.AirPlayLatency, 0, NULL}, + {"iTunesLatency", 'i', POPT_ARG_INT, &config.iTunesLatency, 0, NULL}, + {"forkedDaapdLatency", 0, POPT_ARG_INT, &config.ForkedDaapdLatency, 0, NULL}, + {"stuffing", 'S', POPT_ARG_STRING, &stuffing, 'S', NULL}, + {"resync", 'r', POPT_ARG_INT, &config.resyncthreshold, 0, NULL}, + {"timeout", 't', POPT_ARG_INT, &config.timeout, 't', NULL}, + {"password", 0, POPT_ARG_STRING, &config.password, 0, NULL}, + {"tolerance", 0, POPT_ARG_INT, &config.tolerance, 0, NULL}, #ifdef CONFIG_METADATA - { "metadata-pipename", 'M', POPT_ARG_STRING, &config.metadata_pipename, 'M', NULL } , - { "get-coverart", 'g', POPT_ARG_NONE, &config.get_coverart, 'g', NULL } , + {"metadata-pipename", 'M', POPT_ARG_STRING, &config.metadata_pipename, 'M', NULL}, + {"get-coverart", 'g', POPT_ARG_NONE, &config.get_coverart, 'g', NULL}, #endif - POPT_AUTOHELP + POPT_AUTOHELP #endif - { NULL, 0, 0, NULL, 0 } - }; + {NULL, 0, 0, NULL, 0}}; - - int optind=argc; + int optind = argc; int j; - for (j=0;j<argc;j++) - if (strcmp(argv[j],"--")==0) - optind=j; + for (j = 0; j < argc; j++) + if (strcmp(argv[j], "--") == 0) + optind = j; - optCon = poptGetContext(NULL, optind,(const char **)argv, optionsTable, 0); + optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0); poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); /* Now do options processing, get portname */ - + while ((c = poptGetNextOpt(optCon)) >= 0) { switch (c) { - case 'v': - debuglev++; - break; - case 't': - if (config.timeout==0) { - config.dont_check_timeout=1; - config.allow_session_interruption=1; - } else { - config.dont_check_timeout=0; - config.allow_session_interruption=0; - } - break; + case 'v': + debuglev++; + break; + case 't': + if (config.timeout == 0) { + config.dont_check_timeout = 1; + config.allow_session_interruption = 1; + } else { + config.dont_check_timeout = 0; + config.allow_session_interruption = 0; + } + break; #ifdef CONFIG_METADATA - case 'M': - config.metadata_enabled=1; - break; - case 'g': - if (config.metadata_enabled==0) - die("If you want to get cover art, you must also select the --metadata-pipename option."); - break; -#endif - case 'S': - if (strcmp(stuffing,"basic")==0) - config.packet_stuffing = ST_basic; - else if (strcmp(stuffing,"soxr")==0) + case 'M': + config.metadata_enabled = 1; + break; + case 'g': + if (config.metadata_enabled == 0) + die("If you want to get cover art, you must also select the --metadata-pipename option."); + break; +#endif + case 'S': + if (strcmp(stuffing, "basic") == 0) + config.packet_stuffing = ST_basic; + else if (strcmp(stuffing, "soxr") == 0) #ifdef HAVE_LIBSOXR - config.packet_stuffing = ST_soxr; + config.packet_stuffing = ST_soxr; #else - die("soxr option not available -- this version of shairport-sync was built without libsoxr support"); + die("soxr option not available -- this version of shairport-sync was built without libsoxr " + "support"); #endif - else - die("Illegal stuffing option \"%s\" -- must be \"basic\" or \"soxr\"",stuffing); - break; + else + die("Illegal stuffing option \"%s\" -- must be \"basic\" or \"soxr\"", stuffing); + break; } } if (c < -1) { - die("%s: %s",poptBadOption(optCon, POPT_BADOPTION_NOALIAS),poptStrerror(c)); + die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c)); } #ifdef SUPPORT_CONFIG_FILES - char configuration_file_path[4096]; - strcpy(configuration_file_path,"/etc/"); - strcat(configuration_file_path,appName); - strcat(configuration_file_path,".conf"); - debug(2,"Looking for file \"%s\"",configuration_file_path); - config_setting_t *setting; - const char *str; - int value; - - config_init(&config_file_stuff); - char *cfp = configuration_file_path; - // use the configuration file path given in the -c option, if any - if (config.configfile) - cfp = config.configfile; - /* Read the file. If there is an error, report it and exit. */ - if(config_read_file(&config_file_stuff,cfp)) { - // make config.cfg point to it - config.cfg = &config_file_stuff; - /* Get the Service Name. */ - if(config_lookup_string(config.cfg, "general.name", &str)) - config.apname=(char *)str; - - /* Get the Daemonize setting. */ - if(config_lookup_string(config.cfg, "general.daemonize", &str)) { - if (strcasecmp(str,"no")==0) - config.daemonise=0; - else if (strcasecmp(str,"yes")==0) - config.daemonise=1; - else - die("Invalid daemonize option choice \"%s\". It should be \"yes\" or \"no\""); - } + char configuration_file_path[4096]; + strcpy(configuration_file_path, "/etc/"); + strcat(configuration_file_path, appName); + strcat(configuration_file_path, ".conf"); + debug(2, "Looking for file \"%s\"", configuration_file_path); + config_setting_t *setting; + const char *str; + int value; + + config_init(&config_file_stuff); + char *cfp = configuration_file_path; + // use the configuration file path given in the -c option, if any + if (config.configfile) + cfp = config.configfile; + /* Read the file. If there is an error, report it and exit. */ + if (config_read_file(&config_file_stuff, cfp)) { + // make config.cfg point to it + config.cfg = &config_file_stuff; + /* Get the Service Name. */ + if (config_lookup_string(config.cfg, "general.name", &str)) + config.apname = (char *)str; + + /* Get the Daemonize setting. */ + if (config_lookup_string(config.cfg, "general.daemonize", &str)) { + if (strcasecmp(str, "no") == 0) + config.daemonise = 0; + else if (strcasecmp(str, "yes") == 0) + config.daemonise = 1; + else + die("Invalid daemonize option choice \"%s\". It should be \"yes\" or \"no\""); + } - /* Get the mdns_backend setting. */ - if(config_lookup_string(config.cfg, "general.mdns_backend", &str)) - config.mdns_name=(char *)str; + /* Get the mdns_backend setting. */ + if (config_lookup_string(config.cfg, "general.mdns_backend", &str)) + config.mdns_name = (char *)str; - /* Get the output_backend setting. */ - if(config_lookup_string(config.cfg, "general.output_backend", &str)) - config.output_name=(char *)str; + /* Get the output_backend setting. */ + if (config_lookup_string(config.cfg, "general.output_backend", &str)) + config.output_name = (char *)str; - /* Get the port setting. */ - if(config_lookup_int(config.cfg, "general.port", &value)) { - if ((value<0) || (value>65535)) - die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 5000",value); - else - config.port=value; - } + /* Get the port setting. */ + if (config_lookup_int(config.cfg, "general.port", &value)) { + if ((value < 0) || (value > 65535)) + die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 5000", + value); + else + config.port = value; + } - /* Get the password setting. */ - if(config_lookup_string(config.cfg, "general.password", &str)) - config.password=(char *)str; - - if(config_lookup_string(config.cfg, "general.interpolation", &str)) { - if (strcasecmp(str,"basic")==0) - config.packet_stuffing=ST_basic; - else if (strcasecmp(str,"soxr")==0) - config.packet_stuffing=ST_soxr; - else - die("Invalid interpolation option choice \"%s\". It should be \"basic\" or \"soxr\""); - } + /* Get the password setting. */ + if (config_lookup_string(config.cfg, "general.password", &str)) + config.password = (char *)str; - /* Get the statistics setting. */ - if(config_lookup_string(config.cfg, "general.statistics", &str)) { - if (strcasecmp(str,"no")==0) - config.statistics_requested=0; - else if (strcasecmp(str,"yes")==0) - config.statistics_requested=1; - else - die("Invalid statistics option choice \"%s\". It should be \"yes\" or \"no\""); - } + if (config_lookup_string(config.cfg, "general.interpolation", &str)) { + if (strcasecmp(str, "basic") == 0) + config.packet_stuffing = ST_basic; + else if (strcasecmp(str, "soxr") == 0) + config.packet_stuffing = ST_soxr; + else + die("Invalid interpolation option choice \"%s\". It should be \"basic\" or \"soxr\""); + } - /* Get the drift tolerance setting. */ - if(config_lookup_int(config.cfg, "general.drift", &value)) - config.tolerance=value; - - /* Get the resync setting. */ - if(config_lookup_int(config.cfg, "general.resync_threshold", &value)) - config.resyncthreshold=value; - - /* Get the verbosity setting. */ - if(config_lookup_int(config.cfg, "general.log_verbosity", &value)) - if ((value>=0) && (value<=3)) - debuglev=value; - else - die("Invalid log verbosity setting option choice \"%d\". It should be between 0 and 3, inclusive.",value); - - /* Get the ignore_volume_control setting. */ - if(config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) { - if (strcasecmp(str,"no")==0) - config.ignore_volume_control=0; - else if (strcasecmp(str,"yes")==0) - config.ignore_volume_control=1; - else - die("Invalid ignore_volume_control option choice \"%s\". It should be \"yes\" or \"no\""); - } - - /* Get the default latency. */ - if(config_lookup_int(config.cfg, "latencies.default", &value)) - config.latency=value; + /* Get the statistics setting. */ + if (config_lookup_string(config.cfg, "general.statistics", &str)) { + if (strcasecmp(str, "no") == 0) + config.statistics_requested = 0; + else if (strcasecmp(str, "yes") == 0) + config.statistics_requested = 1; + else + die("Invalid statistics option choice \"%s\". It should be \"yes\" or \"no\""); + } + + /* Get the drift tolerance setting. */ + if (config_lookup_int(config.cfg, "general.drift", &value)) + config.tolerance = value; + + /* Get the resync setting. */ + if (config_lookup_int(config.cfg, "general.resync_threshold", &value)) + config.resyncthreshold = value; + + /* Get the verbosity setting. */ + if (config_lookup_int(config.cfg, "general.log_verbosity", &value)) + if ((value >= 0) && (value <= 3)) + debuglev = value; + else + die("Invalid log verbosity setting option choice \"%d\". It should be between 0 and 3, " + "inclusive.", + value); + + /* Get the ignore_volume_control setting. */ + if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) { + if (strcasecmp(str, "no") == 0) + config.ignore_volume_control = 0; + else if (strcasecmp(str, "yes") == 0) + config.ignore_volume_control = 1; + else + die("Invalid ignore_volume_control option choice \"%s\". It should be \"yes\" or \"no\""); + } + + /* Get the default latency. */ + if (config_lookup_int(config.cfg, "latencies.default", &value)) + config.latency = value; - /* Get the itunes latency. */ - if(config_lookup_int(config.cfg, "latencies.itunes", &value)) - config.iTunesLatency=value; + /* Get the itunes latency. */ + if (config_lookup_int(config.cfg, "latencies.itunes", &value)) + config.iTunesLatency = value; - /* Get the AirPlay latency. */ - if(config_lookup_int(config.cfg, "latencies.airplay", &value)) - config.AirPlayLatency=value; + /* Get the AirPlay latency. */ + if (config_lookup_int(config.cfg, "latencies.airplay", &value)) + config.AirPlayLatency = value; - /* Get the forkedDaapd latency. */ - if(config_lookup_int(config.cfg, "latencies.forkedDaapd", &value)) - config.ForkedDaapdLatency=value; + /* Get the forkedDaapd latency. */ + if (config_lookup_int(config.cfg, "latencies.forkedDaapd", &value)) + config.ForkedDaapdLatency = value; #ifdef CONFIG_METADATA - /* Get the metadata setting. */ - if(config_lookup_string(config.cfg, "metadata.enabled", &str)) { - if (strcasecmp(str,"no")==0) - config.metadata_enabled=0; - else if (strcasecmp(str,"yes")==0) - config.metadata_enabled=1; - else - die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\""); - } - - if(config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) { - if (strcasecmp(str,"no")==0) - config.get_coverart=0; - else if (strcasecmp(str,"yes")==0) - config.get_coverart=1; - else - die("Invalid metadata include_cover_art option choice \"%s\". It should be \"yes\" or \"no\""); - } - - if(config_lookup_string(config.cfg, "metadata.pipe_name", &str)) { - config.metadata_pipename=(char *)str; - } + /* Get the metadata setting. */ + if (config_lookup_string(config.cfg, "metadata.enabled", &str)) { + if (strcasecmp(str, "no") == 0) + config.metadata_enabled = 0; + else if (strcasecmp(str, "yes") == 0) + config.metadata_enabled = 1; + else + die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\""); + } + + if (config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) { + if (strcasecmp(str, "no") == 0) + config.get_coverart = 0; + else if (strcasecmp(str, "yes") == 0) + config.get_coverart = 1; + else + die("Invalid metadata include_cover_art option choice \"%s\". It should be \"yes\" or " + "\"no\""); + } + + if (config_lookup_string(config.cfg, "metadata.pipe_name", &str)) { + config.metadata_pipename = (char *)str; + } #endif - if(config_lookup_string(config.cfg, "sessioncontrol.run_this_before_play_begins", &str)) { - config.cmd_start=(char *)str; - } - - if(config_lookup_string(config.cfg, "sessioncontrol.run_this_after_play_ends", &str)) { - config.cmd_stop=(char *)str; - } - - if(config_lookup_string(config.cfg, "sessioncontrol.wait_for_completion", &str)) { - if (strcasecmp(str,"no")==0) - config.cmd_blocking=0; - else if (strcasecmp(str,"yes")==0) - config.cmd_blocking=1; - else - die("Invalid session control wait_for_completion option choice \"%s\". It should be \"yes\" or \"no\""); - } - - if(config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) { - config.dont_check_timeout=0; // this is for legacy -- only set by -t 0 - if (strcasecmp(str,"no")==0) - config.allow_session_interruption=0; - else if (strcasecmp(str,"yes")==0) - config.allow_session_interruption=1; - else - die("Invalid session control allow_interruption option choice \"%s\". It should be \"yes\" or \"no\""); - } + if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_play_begins", &str)) { + config.cmd_start = (char *)str; + } - if(config_lookup_int(config.cfg, "sessioncontrol.session_timeout", &value)) { - config.timeout=value; - config.dont_check_timeout=0; // this is for legacy -- only set by -t 0 - } + if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_play_ends", &str)) { + config.cmd_stop = (char *)str; + } - } else { - if (config_error_type (&config_file_stuff)==CONFIG_ERR_FILE_IO) - debug(1,"Error reading configuration file \"%s\": \"%s\".",config_error_file(&config_file_stuff),config_error_text(&config_file_stuff)); - else { - die("Line %d of the configuration file \"%s\":\n%s", - config_error_line(&config_file_stuff),config_error_file(&config_file_stuff),config_error_text(&config_file_stuff)); - } + if (config_lookup_string(config.cfg, "sessioncontrol.wait_for_completion", &str)) { + if (strcasecmp(str, "no") == 0) + config.cmd_blocking = 0; + else if (strcasecmp(str, "yes") == 0) + config.cmd_blocking = 1; + else + die("Invalid session control wait_for_completion option choice \"%s\". It should be " + "\"yes\" or \"no\""); + } + + if (config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) { + config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0 + if (strcasecmp(str, "no") == 0) + config.allow_session_interruption = 0; + else if (strcasecmp(str, "yes") == 0) + config.allow_session_interruption = 1; + else + die("Invalid session control allow_interruption option choice \"%s\". It should be \"yes\" " + "or \"no\""); + } + + if (config_lookup_int(config.cfg, "sessioncontrol.session_timeout", &value)) { + config.timeout = value; + config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0 + } + + } else { + if (config_error_type(&config_file_stuff) == CONFIG_ERR_FILE_IO) + debug(1, "Error reading configuration file \"%s\": \"%s\".", + config_error_file(&config_file_stuff), config_error_text(&config_file_stuff)); + else { + die("Line %d of the configuration file \"%s\":\n%s", config_error_line(&config_file_stuff), + config_error_file(&config_file_stuff), config_error_text(&config_file_stuff)); } - + } + #endif /* Print out options */ - - debug(2,"statistics_requester status is %d.",config.statistics_requested); - debug(2,"daemon status is %d.",config.daemonise); - debug(2,"rtsp listening port is %d.",config.port); - debug(2,"Shairport Sync player name is \"%s\".",config.apname); - debug(2,"Audio Output name is \"%s\".",config.output_name); - debug(2,"on-start action is \"%s\".",config.cmd_start); - debug(2,"on-stop action is \"%s\".",config.cmd_stop); - debug(2,"wait-cmd status is %d.",config.cmd_blocking); - debug(2,"mdns backend \"%s\".",config.mdns_name); - debug(2,"userSuppliedLatency is %d.",config.userSuppliedLatency); - debug(2,"AirPlayLatency is %d.",config.AirPlayLatency); - debug(2,"iTunesLatency is %d.",config.iTunesLatency); - debug(2,"forkedDaapdLatency is %d.",config.ForkedDaapdLatency); - debug(2,"stuffing option is \"%d\".",config.packet_stuffing); - debug(2,"resync time is %d.",config.resyncthreshold); - debug(2,"allow a session to be interrupted: %d.",config.allow_session_interruption); - debug(2,"busy timeout time is %d.",config.timeout); - debug(2,"tolerance is %d frames.",config.tolerance); - debug(2,"password is \"%s\".",config.password); - debug(2,"ignore_volume_control is %d.",config.ignore_volume_control); - debug(2,"audio backend desired buffer length is %d.",config.audio_backend_buffer_desired_length); - debug(2,"audio backend latency offset is %d.",config.audio_backend_latency_offset); + + debug(2, "statistics_requester status is %d.", config.statistics_requested); + debug(2, "daemon status is %d.", config.daemonise); + debug(2, "rtsp listening port is %d.", config.port); + debug(2, "Shairport Sync player name is \"%s\".", config.apname); + debug(2, "Audio Output name is \"%s\".", config.output_name); + debug(2, "on-start action is \"%s\".", config.cmd_start); + debug(2, "on-stop action is \"%s\".", config.cmd_stop); + debug(2, "wait-cmd status is %d.", config.cmd_blocking); + debug(2, "mdns backend \"%s\".", config.mdns_name); + debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency); + debug(2, "AirPlayLatency is %d.", config.AirPlayLatency); + debug(2, "iTunesLatency is %d.", config.iTunesLatency); + debug(2, "forkedDaapdLatency is %d.", config.ForkedDaapdLatency); + debug(2, "stuffing option is \"%d\".", config.packet_stuffing); + debug(2, "resync time is %d.", config.resyncthreshold); + debug(2, "allow a session to be interrupted: %d.", config.allow_session_interruption); + debug(2, "busy timeout time is %d.", config.timeout); + debug(2, "tolerance is %d frames.", config.tolerance); + debug(2, "password is \"%s\".", config.password); + debug(2, "ignore_volume_control is %d.", config.ignore_volume_control); + debug(2, "audio backend desired buffer length is %d.", + config.audio_backend_buffer_desired_length); + debug(2, "audio backend latency offset is %d.", config.audio_backend_latency_offset); #ifdef CONFIG_METADATA - debug(2,"metdata enabled is %d.",config.metadata_enabled); - debug(2,"metadata pipename is \"%s\".",config.metadata_pipename); - debug(2,"get-coverart is %d.",config.get_coverart); + debug(2, "metdata enabled is %d.", config.metadata_enabled); + debug(2, "metadata pipename is \"%s\".", config.metadata_pipename); + debug(2, "get-coverart is %d.", config.get_coverart); #endif - return optind+1; + return optind + 1; } void signal_setup(void) { - // mask off all signals before creating threads. - // this way we control which thread gets which signals. - // for now, we don't care which thread gets the following. - sigset_t set; - sigfillset(&set); - sigdelset(&set, SIGINT); - sigdelset(&set, SIGTERM); - sigdelset(&set, SIGHUP); - sigdelset(&set, SIGSTOP); - sigdelset(&set, SIGCHLD); - sigdelset(&set, SIGUSR2); - pthread_sigmask(SIG_BLOCK, &set, NULL); - - // setting this to SIG_IGN would prevent signalling any threads. - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = &sig_ignore; - sigaction(SIGUSR1, &sa, NULL); - - sa.sa_flags = SA_SIGINFO | SA_RESTART; - sa.sa_sigaction = &sig_shutdown; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - sa.sa_sigaction = &sig_disconnect_audio_output; - sigaction(SIGUSR2, &sa, NULL); - - sa.sa_sigaction = &sig_connect_audio_output; - sigaction(SIGHUP, &sa, NULL); - - sa.sa_sigaction = &sig_child; - sigaction(SIGCHLD, &sa, NULL); + // mask off all signals before creating threads. + // this way we control which thread gets which signals. + // for now, we don't care which thread gets the following. + sigset_t set; + sigfillset(&set); + sigdelset(&set, SIGINT); + sigdelset(&set, SIGTERM); + sigdelset(&set, SIGHUP); + sigdelset(&set, SIGSTOP); + sigdelset(&set, SIGCHLD); + sigdelset(&set, SIGUSR2); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + // setting this to SIG_IGN would prevent signalling any threads. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = &sig_ignore; + sigaction(SIGUSR1, &sa, NULL); + + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sa.sa_sigaction = &sig_shutdown; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + sa.sa_sigaction = &sig_disconnect_audio_output; + sigaction(SIGUSR2, &sa, NULL); + + sa.sa_sigaction = &sig_connect_audio_output; + sigaction(SIGHUP, &sa, NULL); + + sa.sa_sigaction = &sig_child; + sigaction(SIGCHLD, &sa, NULL); } // forked daemon lets the spawner know it's up and running OK // should be called only once! void shairport_startup_complete(void) { - if (config.daemonise) { -// daemon_ready(); - } + if (config.daemonise) { + // daemon_ready(); + } } #ifdef USE_CUSTOM_PID_DIR const char *pid_file_proc(void) { #ifdef HAVE_ASPRINTF - static char *fn = NULL; - free(fn); - asprintf(&fn, "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); + static char *fn = NULL; + free(fn); + asprintf(&fn, "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); #else - static char fn[8192]; - snprintf(fn, sizeof(fn), "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); + static char fn[8192]; + snprintf(fn, sizeof(fn), "%s/%s.pid", PIDDIR, + daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); #endif - return fn; + return fn; } #endif @@ -579,222 +597,234 @@ void exit_function() { #ifdef SUPPORT_CONFIG_FILES if (config.cfg) config_destroy(config.cfg); -#endif +#endif } int main(int argc, char **argv) { - daemon_set_verbosity(LOG_DEBUG); - memset(&config, 0, sizeof(config)); // also clears all strings, BTW - atexit(exit_function); - - // set defaults - config.statistics_requested - 0; // don't print stats in the log - config.latency = 88200; // AirPlay. Is also reset in rtsp.c when play is about to start - config.userSuppliedLatency = 0; // zero means none supplied - config.iTunesLatency = 99400; // this seems to work pretty well for iTunes from Version 10 (?) upwards-- two left-ear headphones, one from the iMac jack, one from an NSLU2 running a cheap "3D Sound" USB Soundcard - config.AirPlayLatency = 88200; // this seems to work pretty well for AirPlay -- Syncs sound and vision on AppleTV, but also used for iPhone/iPod/iPad sources - config.ForkedDaapdLatency = 99400; // Seems to be right - config.resyncthreshold = 441*5; // this number of frames is 50 ms - config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle. - config.tolerance = 88; // this number of frames of error before attempting to correct it. - config.buffer_start_fill = 220; - config.port = 5000; - config.packet_stuffing = ST_basic; // simple interpolation or deletion - char hostname[100]; - gethostname(hostname, 100); - config.apname = malloc(20 + 100); - snprintf(config.apname, 20 + 100, "Shairport Sync on %s", hostname); - set_requested_connection_state_to_output(1); // we expect to be able to connect to the output device - config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds. - - // this is a bit weird, but apparently necessary - char* basec = strdup(argv[0]); - char* bname = basename(basec); - appName = strdup(bname); - free(basec); - - /* Check if we are called with -V or --version parameter */ - if (argc >= 2 && ((strcmp(argv[1], "-V")==0) || (strcmp(argv[1], "--version")==0))) { - print_version(); - exit(1); - } + daemon_set_verbosity(LOG_DEBUG); + memset(&config, 0, sizeof(config)); // also clears all strings, BTW + atexit(exit_function); + + // set defaults + config.statistics_requested - 0; // don't print stats in the log + config.latency = 88200; // AirPlay. Is also reset in rtsp.c when play is about to start + config.userSuppliedLatency = 0; // zero means none supplied + config.iTunesLatency = 99400; // this seems to work pretty well for iTunes from Version 10 (?) + // upwards-- two left-ear headphones, one from the iMac jack, one + // from an NSLU2 running a cheap "3D Sound" USB Soundcard + config.AirPlayLatency = 88200; // this seems to work pretty well for AirPlay -- Syncs sound and + // vision on AppleTV, but also used for iPhone/iPod/iPad sources + config.ForkedDaapdLatency = 99400; // Seems to be right + config.resyncthreshold = 441 * 5; // this number of frames is 50 ms + config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle. + config.tolerance = 88; // this number of frames of error before attempting to correct it. + config.buffer_start_fill = 220; + config.port = 5000; + config.packet_stuffing = ST_basic; // simple interpolation or deletion + char hostname[100]; + gethostname(hostname, 100); + config.apname = malloc(20 + 100); + snprintf(config.apname, 20 + 100, "Shairport Sync on %s", hostname); + set_requested_connection_state_to_output( + 1); // we expect to be able to connect to the output device + config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds. + + // this is a bit weird, but apparently necessary + char *basec = strdup(argv[0]); + char *bname = basename(basec); + appName = strdup(bname); + free(basec); + + /* Check if we are called with -V or --version parameter */ + if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) { + print_version(); + exit(1); + } - /* Check if we are called with -h or --help parameter */ - if (argc >= 2 && ((strcmp(argv[1], "-h")==0) || (strcmp(argv[1], "--help")==0))) { - usage(argv[0]); - exit(1); - } + /* Check if we are called with -h or --help parameter */ + if (argc >= 2 && ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0))) { + usage(argv[0]); + exit(1); + } - pid_t pid; + pid_t pid; - /* Reset signal handlers */ - if (daemon_reset_sigs(-1) < 0) { - daemon_log(LOG_ERR, "Failed to reset all signal handlers: %s", strerror(errno)); - return 1; - } + /* Reset signal handlers */ + if (daemon_reset_sigs(-1) < 0) { + daemon_log(LOG_ERR, "Failed to reset all signal handlers: %s", strerror(errno)); + return 1; + } + + /* Unblock signals */ + if (daemon_unblock_sigs(-1) < 0) { + daemon_log(LOG_ERR, "Failed to unblock all signals: %s", strerror(errno)); + return 1; + } - /* Unblock signals */ - if (daemon_unblock_sigs(-1) < 0) { - daemon_log(LOG_ERR, "Failed to unblock all signals: %s", strerror(errno)); - return 1; - } - - #if USE_CUSTOM_LOCAL_STATE_DIR - debug(1,"Locating localstatedir at \"%s\"",LOCALSTATEDIR); - /* Point to a function to help locate where the PID file will go */ - daemon_pid_file_proc = pid_file_proc; -#endif - - /* Set indentification string for the daemon for both syslog and PID file */ - daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]); - - /* Check if we are called with -D or --disconnectFromOutput parameter */ - if (argc >= 2 && ((strcmp(argv[1], "-D")==0) || (strcmp(argv[1], "--disconnectFromOutput")==0))) { - if ((pid = daemon_pid_file_is_running()) >= 0) { - if (kill(pid,SIGUSR2)!=0) { // try to send the signal - daemon_log(LOG_WARNING, "Failed trying to send disconnectFromOutput command to daemon pid: %d: %s",pid, strerror(errno)); - } - } else { - daemon_log(LOG_WARNING, "Can't send a disconnectFromOutput request -- Failed to find daemon: %s", strerror(errno)); + debug(1, "Locating localstatedir at \"%s\"", LOCALSTATEDIR); + /* Point to a function to help locate where the PID file will go */ + daemon_pid_file_proc = pid_file_proc; +#endif + + /* Set indentification string for the daemon for both syslog and PID file */ + daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]); + + /* Check if we are called with -D or --disconnectFromOutput parameter */ + if (argc >= 2 && + ((strcmp(argv[1], "-D") == 0) || (strcmp(argv[1], "--disconnectFromOutput") == 0))) { + if ((pid = daemon_pid_file_is_running()) >= 0) { + if (kill(pid, SIGUSR2) != 0) { // try to send the signal + daemon_log(LOG_WARNING, + "Failed trying to send disconnectFromOutput command to daemon pid: %d: %s", pid, + strerror(errno)); } - exit(1); + } else { + daemon_log(LOG_WARNING, + "Can't send a disconnectFromOutput request -- Failed to find daemon: %s", + strerror(errno)); } - - /* Check if we are called with -R or --reconnectToOutput parameter */ - if (argc >= 2 && ((strcmp(argv[1], "-R")==0) || (strcmp(argv[1], "--reconnectToOutput")==0))) { - if ((pid = daemon_pid_file_is_running()) >= 0) { - if (kill(pid,SIGHUP)!=0) { // try to send the signal - daemon_log(LOG_WARNING, "Failed trying to send reconnectToOutput command to daemon pid: %d: %s",pid, strerror(errno)); - } - } else { - daemon_log(LOG_WARNING, "Can't send a reconnectToOutput request -- Failed to find daemon: %s", strerror(errno)); + exit(1); + } + + /* Check if we are called with -R or --reconnectToOutput parameter */ + if (argc >= 2 && + ((strcmp(argv[1], "-R") == 0) || (strcmp(argv[1], "--reconnectToOutput") == 0))) { + if ((pid = daemon_pid_file_is_running()) >= 0) { + if (kill(pid, SIGHUP) != 0) { // try to send the signal + daemon_log(LOG_WARNING, + "Failed trying to send reconnectToOutput command to daemon pid: %d: %s", pid, + strerror(errno)); } - exit(1); + } else { + daemon_log(LOG_WARNING, "Can't send a reconnectToOutput request -- Failed to find daemon: %s", + strerror(errno)); } - - /* Check if we are called with -k or --kill parameter */ - if (argc >= 2 && ((strcmp(argv[1], "-k")==0) || (strcmp(argv[1], "--kill")==0))) { - int ret; + exit(1); + } - /* Kill daemon with SIGTERM */ - /* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */ - if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0) - daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno)); - else - daemon_pid_file_remove(); - return ret < 0 ? 1 : 0; - } + /* Check if we are called with -k or --kill parameter */ + if (argc >= 2 && ((strcmp(argv[1], "-k") == 0) || (strcmp(argv[1], "--kill") == 0))) { + int ret; + + /* Kill daemon with SIGTERM */ + /* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */ + if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0) + daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno)); + else + daemon_pid_file_remove(); + return ret < 0 ? 1 : 0; + } - /* Check that the daemon is not running twice at the same time */ - if ((pid = daemon_pid_file_is_running()) >= 0) { - daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); - return 1; + /* Check that the daemon is not running twice at the same time */ + if ((pid = daemon_pid_file_is_running()) >= 0) { + daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); + return 1; + } + + // parse arguments into config + int audio_arg = parse_options(argc, argv); + + // mDNS supports maximum of 63-character names (we append 13). + if (strlen(config.apname) > 50) + die("Supplied name too long (max 50 characters)"); + + /* here, daemonise with libdaemon */ + + if (config.daemonise) { + /* Prepare for return value passing from the initialization procedure of the daemon process */ + if (daemon_retval_init() < 0) { + daemon_log(LOG_ERR, "Failed to create pipe."); + return 1; } - // parse arguments into config - int audio_arg = parse_options(argc, argv); - - // mDNS supports maximum of 63-character names (we append 13). - if (strlen(config.apname) > 50) - die("Supplied name too long (max 50 characters)"); - - /* here, daemonise with libdaemon */ - - if (config.daemonise) { - /* Prepare for return value passing from the initialization procedure of the daemon process */ - if (daemon_retval_init() < 0) { - daemon_log(LOG_ERR, "Failed to create pipe."); - return 1; + /* Do the fork */ + if ((pid = daemon_fork()) < 0) { + + /* Exit on error */ + daemon_retval_done(); + return 1; + + } else if (pid) { /* The parent */ + int ret; + + /* Wait for 20 seconds for the return value passed from the daemon process */ + if ((ret = daemon_retval_wait(20)) < 0) { + daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", + strerror(errno)); + return 255; } - /* Do the fork */ - if ((pid = daemon_fork()) < 0) { - - /* Exit on error */ - daemon_retval_done(); - return 1; - - } else if (pid) { /* The parent */ - int ret; - - /* Wait for 20 seconds for the return value passed from the daemon process */ - if ((ret = daemon_retval_wait(20)) < 0) { - daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", strerror(errno)); - return 255; - } - - if (ret != 0) - daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret); - return ret; - - } else { /* The daemon */ - - /* Close FDs */ - if (daemon_close_all(-1) < 0) { - daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); - - /* Send the error condition to the parent process */ - daemon_retval_send(1); - goto finish; - } - - /* Create the PID file */ - if (daemon_pid_file_create() < 0) { - daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); - daemon_retval_send(2); - goto finish; - } - - /* Send OK to parent process */ - daemon_retval_send(0); + if (ret != 0) + daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret); + return ret; + + } else { /* The daemon */ + + /* Close FDs */ + if (daemon_close_all(-1) < 0) { + daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); + + /* Send the error condition to the parent process */ + daemon_retval_send(1); + goto finish; } - /* end libdaemon stuff */ - } - signal_setup(); - - // make sure the program can create files that group and world can read - umask(S_IWGRP | S_IWOTH); + /* Create the PID file */ + if (daemon_pid_file_create() < 0) { + daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); + daemon_retval_send(2); + goto finish; + } - config.output = audio_get_output(config.output_name); - if (!config.output) { - audio_ls_outputs(); - die("Invalid audio output specified!"); + /* Send OK to parent process */ + daemon_retval_send(0); } - config.output->init(argc-audio_arg, argv+audio_arg); + /* end libdaemon stuff */ + } - daemon_log(LOG_NOTICE, "startup"); + signal_setup(); - uint8_t ap_md5[16]; + // make sure the program can create files that group and world can read + umask(S_IWGRP | S_IWOTH); + + config.output = audio_get_output(config.output_name); + if (!config.output) { + audio_ls_outputs(); + die("Invalid audio output specified!"); + } + config.output->init(argc - audio_arg, argv + audio_arg); + + daemon_log(LOG_NOTICE, "startup"); + + uint8_t ap_md5[16]; #ifdef HAVE_LIBSSL - MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, config.apname, strlen(config.apname)); - MD5_Final(ap_md5, &ctx); + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, config.apname, strlen(config.apname)); + MD5_Final(ap_md5, &ctx); #endif #ifdef HAVE_LIBPOLARSSL - md5_context tctx; - md5_starts(&tctx); - md5_update(&tctx, (unsigned char *)config.apname, strlen(config.apname)); - md5_finish(&tctx, ap_md5); + md5_context tctx; + md5_starts(&tctx); + md5_update(&tctx, (unsigned char *)config.apname, strlen(config.apname)); + md5_finish(&tctx, ap_md5); #endif - memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr)); + memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr)); #ifdef CONFIG_METADATA - metadata_init() ; // create the metadata pipe if necessary + metadata_init(); // create the metadata pipe if necessary #endif - rtsp_listen_loop(); + rtsp_listen_loop(); - // should not reach this... - shairport_shutdown(); + // should not reach this... + shairport_shutdown(); finish: - daemon_log(LOG_NOTICE, "Unexpected exit..."); - daemon_retval_send(255); - daemon_pid_file_remove(); - return 1; - + daemon_log(LOG_NOTICE, "Unexpected exit..."); + daemon_retval_send(255); + daemon_pid_file_remove(); + return 1; } diff --git a/tinysvcmdns.c b/tinysvcmdns.c index 8c03b38..bdb6c09 100644 --- a/tinysvcmdns.c +++ b/tinysvcmdns.c @@ -34,18 +34,16 @@ #include "common.h" #define DEBUG_PRINTF(...) debug(1, __VA_ARGS__) -#define log_message(level, ...) \ - do { \ - switch(level) \ - { \ - case LOG_ERR: \ - warn(__VA_ARGS__); \ - break; \ - default: \ - debug(1, __VA_ARGS__); \ - } \ - } while (0) - +#define log_message(level, ...) \ + do { \ + switch (level) { \ + case LOG_ERR: \ + warn(__VA_ARGS__); \ + break; \ + default: \ + debug(1, __VA_ARGS__); \ + } \ + } while (0) //******************************************************// // mdns.c // @@ -63,311 +61,320 @@ #include <netinet/in.h> #endif -// See RFC 6762 Section 10 for an account of two TTLs -- 120 seconds for rrs with a host name as the record's name +// See RFC 6762 Section 10 for an account of two TTLs -- 120 seconds for rrs with a host name as the +// record's name // or a host name in the record's rdata // 75 minutes for everything else. // https://tools.ietf.org/html/rfc6762 -#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120 -#define DEFAULT_TTL 4500 +#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120 +#define DEFAULT_TTL 4500 struct name_comp { - uint8_t *label; // label - size_t pos; // position in msg + uint8_t *label; // label + size_t pos; // position in msg - struct name_comp *next; + struct name_comp *next; }; // ----- label functions ----- // duplicates a name inline uint8_t *dup_nlabel(const uint8_t *n) { - assert(n[0] <= 63); // prevent mis-use - return (uint8_t *) strdup((char *) n); + assert(n[0] <= 63); // prevent mis-use + return (uint8_t *)strdup((char *)n); } // duplicates a label uint8_t *dup_label(const uint8_t *label) { - int len = *label + 1; - if (len > 63) - return NULL; - uint8_t *newlabel = malloc(len + 1); - strncpy((char *) newlabel, (char *) label, len); - newlabel[len] = '\0'; - return newlabel; + int len = *label + 1; + if (len > 63) + return NULL; + uint8_t *newlabel = malloc(len + 1); + strncpy((char *)newlabel, (char *)label, len); + newlabel[len] = '\0'; + return newlabel; } uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) { - int len1, len2; - uint8_t *s; + int len1, len2; + uint8_t *s; - assert(n1[0] <= 63 && n2[0] <= 63); // detect misuse + assert(n1[0] <= 63 && n2[0] <= 63); // detect misuse - len1 = strlen((char *) n1); - len2 = strlen((char *) n2); + len1 = strlen((char *)n1); + len2 = strlen((char *)n2); - s = malloc(len1 + len2 + 1); - strncpy((char *) s, (char *) n1, len1); - strncpy((char *) s+len1, (char *) n2, len2); - s[len1 + len2] = '\0'; - return s; + s = malloc(len1 + len2 + 1); + strncpy((char *)s, (char *)n1, len1); + strncpy((char *)s + len1, (char *)n2, len2); + s[len1 + len2] = '\0'; + return s; } // returns a human-readable name label in dotted form char *nlabel_to_str(const uint8_t *name) { - char *label, *labelp; - const uint8_t *p; + char *label, *labelp; + const uint8_t *p; - assert(name != NULL); + assert(name != NULL); - label = labelp = malloc(256); + label = labelp = malloc(256); - for (p = name; *p; p++) { - strncpy(labelp, (char *) p + 1, *p); - labelp += *p; - *labelp = '.'; - labelp++; + for (p = name; *p; p++) { + strncpy(labelp, (char *)p + 1, *p); + labelp += *p; + *labelp = '.'; + labelp++; - p += *p; - } + p += *p; + } - *labelp = '\0'; + *labelp = '\0'; - return label; + return label; } // returns the length of a label field // does NOT uncompress the field, so it could be as small as 2 bytes // or 1 for the root static size_t label_len(uint8_t *pkt_buf, size_t pkt_len, size_t off) { - uint8_t *p; - uint8_t *e = pkt_buf + pkt_len; - size_t len = 0; - - for (p = pkt_buf + off; p < e; p++) { - if (*p == 0) { - return len + 1; - } else if ((*p & 0xC0) == 0xC0) { - return len + 2; - } else { - len += *p + 1; - p += *p; - } - } - - return len; + uint8_t *p; + uint8_t *e = pkt_buf + pkt_len; + size_t len = 0; + + for (p = pkt_buf + off; p < e; p++) { + if (*p == 0) { + return len + 1; + } else if ((*p & 0xC0) == 0xC0) { + return len + 2; + } else { + len += *p + 1; + p += *p; + } + } + + return len; } // creates a label // free() after use uint8_t *create_label(const char *txt) { - int len; - uint8_t *s; + int len; + uint8_t *s; - assert(txt != NULL); - len = strlen(txt); - if (len > 63) - return NULL; + assert(txt != NULL); + len = strlen(txt); + if (len > 63) + return NULL; - s = malloc(len + 2); - s[0] = len; - strncpy((char *) s + 1, txt, len); - s[len + 1] = '\0'; + s = malloc(len + 2); + s[0] = len; + strncpy((char *)s + 1, txt, len); + s[len + 1] = '\0'; - return s; + return s; } // creates a uncompressed name label given a DNS name like "apple.b.com" // free() after use uint8_t *create_nlabel(const char *name) { - char *label; - char *p, *e, *lenpos; - int len = 0; + char *label; + char *p, *e, *lenpos; + int len = 0; - assert(name != NULL); + assert(name != NULL); - len = strlen(name); - label = malloc(len + 1 + 1); - if (label == NULL) - return NULL; + len = strlen(name); + label = malloc(len + 1 + 1); + if (label == NULL) + return NULL; - strncpy((char *) label + 1, name, len); - label[len + 1] = '\0'; + strncpy((char *)label + 1, name, len); + label[len + 1] = '\0'; - p = label; - e = p + len; - lenpos = p; + p = label; + e = p + len; + lenpos = p; - while (p < e) { - *lenpos = 0; - char *dot = memchr(p + 1, '.', e - p - 1); - if (dot == NULL) - dot = e + 1; - *lenpos = dot - p - 1; + while (p < e) { + *lenpos = 0; + char *dot = memchr(p + 1, '.', e - p - 1); + if (dot == NULL) + dot = e + 1; + *lenpos = dot - p - 1; - p = dot; - lenpos = dot; - } + p = dot; + lenpos = dot; + } - return (uint8_t *) label; + return (uint8_t *)label; } // copies a label from the buffer into a newly-allocated string // free() after use static uint8_t *copy_label(uint8_t *pkt_buf, size_t pkt_len, size_t off) { - int len; + int len; - if (off > pkt_len) - return NULL; + if (off > pkt_len) + return NULL; - len = pkt_buf[off] + 1; - if (off + len > pkt_len) { - DEBUG_PRINTF("label length exceeds packet buffer\n"); - return NULL; - } + len = pkt_buf[off] + 1; + if (off + len > pkt_len) { + DEBUG_PRINTF("label length exceeds packet buffer\n"); + return NULL; + } - return dup_label(pkt_buf + off); + return dup_label(pkt_buf + off); } // uncompresses a name // free() after use static uint8_t *uncompress_nlabel(uint8_t *pkt_buf, size_t pkt_len, size_t off) { - uint8_t *p; - uint8_t *e = pkt_buf + pkt_len; - size_t len = 0; - char *str, *sp; - if (off >= pkt_len) - return NULL; - - // calculate length of uncompressed label - for (p = pkt_buf + off; *p && p < e; p++) { - size_t llen = 0; - if ((*p & 0xC0) == 0xC0) { - uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]); - llen = *p2 + 1; - p = p2 + llen - 1; - } else { - llen = *p + 1; - p += llen - 1; - } - len += llen; - } - - str = sp = malloc(len + 1); - if (str == NULL) - return NULL; - - // FIXME: must merge this with above code - for (p = pkt_buf + off; *p && p < e; p++) { - size_t llen = 0; - if ((*p & 0xC0) == 0xC0) { - uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]); - llen = *p2 + 1; - strncpy(sp, (char *) p2, llen); - p = p2 + llen - 1; - } else { - llen = *p + 1; - strncpy(sp, (char *) p, llen); - p += llen - 1; - } - sp += llen; - } - *sp = '\0'; - - return (uint8_t *) str; + uint8_t *p; + uint8_t *e = pkt_buf + pkt_len; + size_t len = 0; + char *str, *sp; + if (off >= pkt_len) + return NULL; + + // calculate length of uncompressed label + for (p = pkt_buf + off; *p && p < e; p++) { + size_t llen = 0; + if ((*p & 0xC0) == 0xC0) { + uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]); + llen = *p2 + 1; + p = p2 + llen - 1; + } else { + llen = *p + 1; + p += llen - 1; + } + len += llen; + } + + str = sp = malloc(len + 1); + if (str == NULL) + return NULL; + + // FIXME: must merge this with above code + for (p = pkt_buf + off; *p && p < e; p++) { + size_t llen = 0; + if ((*p & 0xC0) == 0xC0) { + uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]); + llen = *p2 + 1; + strncpy(sp, (char *)p2, llen); + p = p2 + llen - 1; + } else { + llen = *p + 1; + strncpy(sp, (char *)p, llen); + p += llen - 1; + } + sp += llen; + } + *sp = '\0'; + + return (uint8_t *)str; } // ----- RR list & group functions ----- const char *rr_get_type_name(enum rr_type type) { - switch (type) { - case RR_A: return "A"; - case RR_PTR: return "PTR"; - case RR_TXT: return "TXT"; - case RR_AAAA: return "AAAA"; - case RR_SRV: return "SRV"; - case RR_NSEC: return "NSEC"; - case RR_ANY: return "ANY"; - } - return NULL; + switch (type) { + case RR_A: + return "A"; + case RR_PTR: + return "PTR"; + case RR_TXT: + return "TXT"; + case RR_AAAA: + return "AAAA"; + case RR_SRV: + return "SRV"; + case RR_NSEC: + return "NSEC"; + case RR_ANY: + return "ANY"; + } + return NULL; } void rr_entry_destroy(struct rr_entry *rr) { - struct rr_data_txt *txt_rec; - assert(rr); - - // check rr_type and free data elements - switch (rr->type) { - case RR_PTR: - if (rr->data.PTR.name) - free(rr->data.PTR.name); - // don't free entry - break; - - case RR_TXT: - txt_rec = &rr->data.TXT; - while (txt_rec) { - struct rr_data_txt *next = txt_rec->next; - if (txt_rec->txt) - free(txt_rec->txt); - - // only free() if it wasn't part of the struct - if (txt_rec != &rr->data.TXT) - free(txt_rec); - - txt_rec = next; - } - break; - - case RR_SRV: - if (rr->data.SRV.target) - free(rr->data.SRV.target); - break; - - default: - // nothing to free - break; - } - - free(rr->name); - free(rr); + struct rr_data_txt *txt_rec; + assert(rr); + + // check rr_type and free data elements + switch (rr->type) { + case RR_PTR: + if (rr->data.PTR.name) + free(rr->data.PTR.name); + // don't free entry + break; + + case RR_TXT: + txt_rec = &rr->data.TXT; + while (txt_rec) { + struct rr_data_txt *next = txt_rec->next; + if (txt_rec->txt) + free(txt_rec->txt); + + // only free() if it wasn't part of the struct + if (txt_rec != &rr->data.TXT) + free(txt_rec); + + txt_rec = next; + } + break; + + case RR_SRV: + if (rr->data.SRV.target) + free(rr->data.SRV.target); + break; + + default: + // nothing to free + break; + } + + free(rr->name); + free(rr); } // destroys an RR list (and optionally, items) void rr_list_destroy(struct rr_list *rr, char destroy_items) { - struct rr_list *rr_next; - - for (; rr; rr = rr_next) { - rr_next = rr->next; - if (destroy_items) - rr_entry_destroy(rr->e); - free(rr); - } + struct rr_list *rr_next; + + for (; rr; rr = rr_next) { + rr_next = rr->next; + if (destroy_items) + rr_entry_destroy(rr->e); + free(rr); + } } int rr_list_count(struct rr_list *rr) { - int i = 0; - for (; rr; i++, rr = rr->next); - return i; + int i = 0; + for (; rr; i++, rr = rr->next) + ; + return i; } struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) { - struct rr_list *le = *rr_head, *pe = NULL; - for (; le; le = le->next) { - if (le->e == rr) { - if (pe == NULL) { - *rr_head = le->next; - free(le); - return rr; - } else { - pe->next = le->next; - free(le); - return rr; - } - } - pe = le; - } - return NULL; + struct rr_list *le = *rr_head, *pe = NULL; + for (; le; le = le->next) { + if (le->e == rr) { + if (pe == NULL) { + *rr_head = le->next; + free(le); + return rr; + } else { + pe->next = le->next; + free(le); + return rr; + } + } + pe = le; + } + return NULL; } // appends an rr_entry to an RR list @@ -375,650 +382,642 @@ struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) { // RRs are compared by memory location - not its contents // return value of 0 means item not added int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr) { - struct rr_list *node = malloc(sizeof(struct rr_list)); - node->e = rr; - node->next = NULL; - - if (*rr_head == NULL) { - *rr_head = node; - } else { - struct rr_list *e = *rr_head, *taile; - for (; e; e = e->next) { - // already in list - don't add - if (e->e == rr) { - free(node); - return 0; - } - if (e->next == NULL) - taile = e; - } - taile->next = node; - } - return 1; + struct rr_list *node = malloc(sizeof(struct rr_list)); + node->e = rr; + node->next = NULL; + + if (*rr_head == NULL) { + *rr_head = node; + } else { + struct rr_list *e = *rr_head, *taile; + for (; e; e = e->next) { + // already in list - don't add + if (e->e == rr) { + free(node); + return 0; + } + if (e->next == NULL) + taile = e; + } + taile->next = node; + } + return 1; } -#define FILL_RR_ENTRY(rr, _name, _type) \ - rr->name = _name; \ - rr->type = _type; \ - rr->ttl = DEFAULT_TTL; \ - rr->cache_flush = 1; \ - rr->rr_class = 1; +#define FILL_RR_ENTRY(rr, _name, _type) \ + rr->name = _name; \ + rr->type = _type; \ + rr->ttl = DEFAULT_TTL; \ + rr->cache_flush = 1; \ + rr->rr_class = 1; struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr) { - DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); - FILL_RR_ENTRY(rr, name, RR_A); - rr->data.A.addr = addr; - rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10 - return rr; + DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); + FILL_RR_ENTRY(rr, name, RR_A); + rr->data.A.addr = addr; + rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10 + return rr; } struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr) { - DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); - FILL_RR_ENTRY(rr, name, RR_AAAA); - rr->data.AAAA.addr = addr; - rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10 - return rr; + DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); + FILL_RR_ENTRY(rr, name, RR_AAAA); + rr->data.AAAA.addr = addr; + rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10 + return rr; } struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target) { - DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); - FILL_RR_ENTRY(rr, name, RR_SRV); - rr->data.SRV.port = port; - rr->data.SRV.target = target; - return rr; + DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); + FILL_RR_ENTRY(rr, name, RR_SRV); + rr->data.SRV.port = port; + rr->data.SRV.target = target; + return rr; } struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr) { - DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); - FILL_RR_ENTRY(rr, name, RR_PTR); - rr->cache_flush = 0; // PTRs shouldn't have their cache flush bit set - rr->data.PTR.entry = d_rr; - return rr; + DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); + FILL_RR_ENTRY(rr, name, RR_PTR); + rr->cache_flush = 0; // PTRs shouldn't have their cache flush bit set + rr->data.PTR.entry = d_rr; + return rr; } struct rr_entry *rr_create(uint8_t *name, enum rr_type type) { - DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); - FILL_RR_ENTRY(rr, name, type); - return rr; + DECL_MALLOC_ZERO_STRUCT(rr, rr_entry); + FILL_RR_ENTRY(rr, name, type); + return rr; } void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type) { - assert(rr_nsec->type = RR_NSEC); - assert((type / 8) < sizeof(rr_nsec->data.NSEC.bitmap)); + assert(rr_nsec->type = RR_NSEC); + assert((type / 8) < sizeof(rr_nsec->data.NSEC.bitmap)); - rr_nsec->data.NSEC.bitmap[ type / 8 ] = 1 << (7 - (type % 8)); + rr_nsec->data.NSEC.bitmap[type / 8] = 1 << (7 - (type % 8)); } void rr_add_txt(struct rr_entry *rr_txt, const char *txt) { - struct rr_data_txt *txt_rec; - assert(rr_txt->type == RR_TXT); + struct rr_data_txt *txt_rec; + assert(rr_txt->type == RR_TXT); - txt_rec = &rr_txt->data.TXT; + txt_rec = &rr_txt->data.TXT; - // is current data filled? - if (txt_rec->txt == NULL) { - txt_rec->txt = create_label(txt); - return; - } + // is current data filled? + if (txt_rec->txt == NULL) { + txt_rec->txt = create_label(txt); + return; + } - // find the last node - for (; txt_rec->next; txt_rec = txt_rec->next); + // find the last node + for (; txt_rec->next; txt_rec = txt_rec->next) + ; - // create a new empty node - txt_rec->next = malloc(sizeof(struct rr_data_txt)); + // create a new empty node + txt_rec->next = malloc(sizeof(struct rr_data_txt)); - txt_rec = txt_rec->next; - txt_rec->txt = create_label(txt); - txt_rec->next = NULL; + txt_rec = txt_rec->next; + txt_rec->txt = create_label(txt); + txt_rec->next = NULL; } // adds a record to an rr_group void rr_group_add(struct rr_group **group, struct rr_entry *rr) { - struct rr_group *g; + struct rr_group *g; - assert(rr != NULL); + assert(rr != NULL); - if (*group) { - g = rr_group_find(*group, rr->name); - if (g) { - rr_list_append(&g->rr, rr); - return; - } - } + if (*group) { + g = rr_group_find(*group, rr->name); + if (g) { + rr_list_append(&g->rr, rr); + return; + } + } - MALLOC_ZERO_STRUCT(g, rr_group); - g->name = dup_nlabel(rr->name); - rr_list_append(&g->rr, rr); + MALLOC_ZERO_STRUCT(g, rr_group); + g->name = dup_nlabel(rr->name); + rr_list_append(&g->rr, rr); - // prepend to list - g->next = *group; - *group = g; + // prepend to list + g->next = *group; + *group = g; } // finds a rr_group matching the given name -struct rr_group *rr_group_find(struct rr_group* g, uint8_t *name) { - for (; g; g = g->next) { - if (cmp_nlabel(g->name, name) == 0) - return g; - } - return NULL; +struct rr_group *rr_group_find(struct rr_group *g, uint8_t *name) { + for (; g; g = g->next) { + if (cmp_nlabel(g->name, name) == 0) + return g; + } + return NULL; } struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type) { - struct rr_list *rr = rr_list; - for (; rr; rr = rr->next) { - if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0) - return rr->e; - } - return NULL; + struct rr_list *rr = rr_list; + for (; rr; rr = rr->next) { + if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0) + return rr->e; + } + return NULL; } // looks for a matching entry in rr_list // if entry is a PTR, we need to check if the PTR target also matches struct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry) { - struct rr_list *rr = rr_list; - for (; rr; rr = rr->next) { - if (rr->e->type == entry->type && cmp_nlabel(rr->e->name, entry->name) == 0) { - if (entry->type != RR_PTR) { - return rr->e; - } else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry), MDNS_RR_GET_PTR_NAME(rr->e)) == 0) { - // if it's a PTR, we need to make sure PTR target also matches - return rr->e; - } - } - } - return NULL; + struct rr_list *rr = rr_list; + for (; rr; rr = rr->next) { + if (rr->e->type == entry->type && cmp_nlabel(rr->e->name, entry->name) == 0) { + if (entry->type != RR_PTR) { + return rr->e; + } else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry), MDNS_RR_GET_PTR_NAME(rr->e)) == 0) { + // if it's a PTR, we need to make sure PTR target also matches + return rr->e; + } + } + } + return NULL; } void rr_group_destroy(struct rr_group *group) { - struct rr_group *g = group; - - while (g) { - struct rr_group *nextg = g->next; - free(g->name); - rr_list_destroy(g->rr, 1); - free(g); - g = nextg; - } + struct rr_group *g = group; + + while (g) { + struct rr_group *nextg = g->next; + free(g->name); + rr_list_destroy(g->rr, 1); + free(g); + g = nextg; + } } uint8_t *mdns_write_u16(uint8_t *ptr, const uint16_t v) { - *ptr++ = (uint8_t) (v >> 8) & 0xFF; - *ptr++ = (uint8_t) (v >> 0) & 0xFF; - return ptr; + *ptr++ = (uint8_t)(v >> 8) & 0xFF; + *ptr++ = (uint8_t)(v >> 0) & 0xFF; + return ptr; } uint8_t *mdns_write_u32(uint8_t *ptr, const uint32_t v) { - *ptr++ = (uint8_t) (v >> 24) & 0xFF; - *ptr++ = (uint8_t) (v >> 16) & 0xFF; - *ptr++ = (uint8_t) (v >> 8) & 0xFF; - *ptr++ = (uint8_t) (v >> 0) & 0xFF; - return ptr; + *ptr++ = (uint8_t)(v >> 24) & 0xFF; + *ptr++ = (uint8_t)(v >> 16) & 0xFF; + *ptr++ = (uint8_t)(v >> 8) & 0xFF; + *ptr++ = (uint8_t)(v >> 0) & 0xFF; + return ptr; } uint16_t mdns_read_u16(const uint8_t *ptr) { - return ((ptr[0] & 0xFF) << 8) | - ((ptr[1] & 0xFF) << 0); + return ((ptr[0] & 0xFF) << 8) | ((ptr[1] & 0xFF) << 0); } uint32_t mdns_read_u32(const uint8_t *ptr) { - return ((ptr[0] & 0xFF) << 24) | - ((ptr[1] & 0xFF) << 16) | - ((ptr[2] & 0xFF) << 8) | - ((ptr[3] & 0xFF) << 0); + return ((ptr[0] & 0xFF) << 24) | ((ptr[1] & 0xFF) << 16) | ((ptr[2] & 0xFF) << 8) | + ((ptr[3] & 0xFF) << 0); } // initialize the packet for reply // clears the packet of list structures but not its list items void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id) { - // copy transaction ID - pkt->id = id; - - // response flags - pkt->flags = MDNS_FLAG_RESP | MDNS_FLAG_AA; - - rr_list_destroy(pkt->rr_qn, 0); - rr_list_destroy(pkt->rr_ans, 0); - rr_list_destroy(pkt->rr_auth, 0); - rr_list_destroy(pkt->rr_add, 0); - - pkt->rr_qn = NULL; - pkt->rr_ans = NULL; - pkt->rr_auth = NULL; - pkt->rr_add = NULL; - - pkt->num_qn = 0; - pkt->num_ans_rr = 0; - pkt->num_auth_rr = 0; - pkt->num_add_rr = 0; + // copy transaction ID + pkt->id = id; + + // response flags + pkt->flags = MDNS_FLAG_RESP | MDNS_FLAG_AA; + + rr_list_destroy(pkt->rr_qn, 0); + rr_list_destroy(pkt->rr_ans, 0); + rr_list_destroy(pkt->rr_auth, 0); + rr_list_destroy(pkt->rr_add, 0); + + pkt->rr_qn = NULL; + pkt->rr_ans = NULL; + pkt->rr_auth = NULL; + pkt->rr_add = NULL; + + pkt->num_qn = 0; + pkt->num_ans_rr = 0; + pkt->num_auth_rr = 0; + pkt->num_add_rr = 0; } // destroys an mdns_pkt struct, including its contents void mdns_pkt_destroy(struct mdns_pkt *p) { - rr_list_destroy(p->rr_qn, 1); - rr_list_destroy(p->rr_ans, 1); - rr_list_destroy(p->rr_auth, 1); - rr_list_destroy(p->rr_add, 1); + rr_list_destroy(p->rr_qn, 1); + rr_list_destroy(p->rr_ans, 1); + rr_list_destroy(p->rr_auth, 1); + rr_list_destroy(p->rr_add, 1); - free(p); + free(p); } - // parse the MDNS questions section // stores the parsed data in the given mdns_pkt struct -static size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off, - struct mdns_pkt *pkt) { - const uint8_t *p = pkt_buf + off; - struct rr_entry *rr; - uint8_t *name; +static size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct mdns_pkt *pkt) { + const uint8_t *p = pkt_buf + off; + struct rr_entry *rr; + uint8_t *name; - assert(pkt != NULL); + assert(pkt != NULL); - rr = malloc(sizeof(struct rr_entry)); - memset(rr, 0, sizeof(struct rr_entry)); + rr = malloc(sizeof(struct rr_entry)); + memset(rr, 0, sizeof(struct rr_entry)); - name = uncompress_nlabel(pkt_buf, pkt_len, off); - p += label_len(pkt_buf, pkt_len, off); - rr->name = name; + name = uncompress_nlabel(pkt_buf, pkt_len, off); + p += label_len(pkt_buf, pkt_len, off); + rr->name = name; - rr->type = mdns_read_u16(p); - p += sizeof(uint16_t); + rr->type = mdns_read_u16(p); + p += sizeof(uint16_t); - rr->unicast_query = (*p & 0x80) == 0x80; - rr->rr_class = mdns_read_u16(p) & ~0x80; - p += sizeof(uint16_t); + rr->unicast_query = (*p & 0x80) == 0x80; + rr->rr_class = mdns_read_u16(p) & ~0x80; + p += sizeof(uint16_t); - rr_list_append(&pkt->rr_qn, rr); + rr_list_append(&pkt->rr_qn, rr); - return p - (pkt_buf + off); + return p - (pkt_buf + off); } // parse the MDNS RR section // stores the parsed data in the given mdns_pkt struct -static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, - struct mdns_pkt *pkt) { - const uint8_t *p = pkt_buf + off; - const uint8_t *e = pkt_buf + pkt_len; - struct rr_entry *rr; - uint8_t *name; - size_t rr_data_len = 0; - struct rr_data_txt *txt_rec; - int parse_error = 0; - - assert(pkt != NULL); - - if (off > pkt_len) - return 0; - - rr = malloc(sizeof(struct rr_entry)); - memset(rr, 0, sizeof(struct rr_entry)); - - name = uncompress_nlabel(pkt_buf, pkt_len, off); - p += label_len(pkt_buf, pkt_len, off); - rr->name = name; - - rr->type = mdns_read_u16(p); - p += sizeof(uint16_t); - - rr->cache_flush = (*p & 0x80) == 0x80; - rr->rr_class = mdns_read_u16(p) & ~0x80; - p += sizeof(uint16_t); - - rr->ttl = mdns_read_u32(p); - p += sizeof(uint32_t); - - // RR data - rr_data_len = mdns_read_u16(p); - p += sizeof(uint16_t); - - if (p + rr_data_len > e) { - DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len, e - p); - rr_entry_destroy(rr); - return 0; - } - - e = p + rr_data_len; - - // see if we can parse the RR data - switch (rr->type) { - case RR_A: - if (rr_data_len < sizeof(uint32_t)) { - DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len); - parse_error = 1; - break; - } - rr->data.A.addr = ntohl(mdns_read_u32(p)); /* addr already in net order */ - p += sizeof(uint32_t); - break; - - case RR_AAAA: - if (rr_data_len < sizeof(struct in6_addr)) { - DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len); - parse_error = 1; - break; - } - rr->data.AAAA.addr = malloc(sizeof(struct in6_addr)); - int i; - for (i = 0; i < sizeof(struct in6_addr); i++) - rr->data.AAAA.addr->s6_addr[i] = p[i]; - p += sizeof(struct in6_addr); - break; - - case RR_PTR: - rr->data.PTR.name = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf); - if (rr->data.PTR.name == NULL) { - DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n"); - parse_error = 1; - break; - } - p += rr_data_len; - break; - - case RR_TXT: - txt_rec = &rr->data.TXT; - - // not supposed to happen, but we should handle it - if (rr_data_len == 0) { - DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n"); - txt_rec->txt = create_label(""); - break; - } - - while (1) { - txt_rec->txt = copy_label(pkt_buf, pkt_len, p - pkt_buf); - if (txt_rec->txt == NULL) { - DEBUG_PRINTF("unable to copy label for TXT record\n"); - parse_error = 1; - break; - } - p += txt_rec->txt[0] + 1; - - if (p >= e) - break; - - // allocate another record - txt_rec->next = malloc(sizeof(struct rr_data_txt)); - txt_rec = txt_rec->next; - txt_rec->next = NULL; - } - break; - - default: - // skip to end of RR data - p = e; - } - - // if there was a parse error, destroy partial rr_entry - if (parse_error) { - rr_entry_destroy(rr); - return 0; - } - - rr_list_append(&pkt->rr_ans, rr); - - return p - (pkt_buf + off); +static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct mdns_pkt *pkt) { + const uint8_t *p = pkt_buf + off; + const uint8_t *e = pkt_buf + pkt_len; + struct rr_entry *rr; + uint8_t *name; + size_t rr_data_len = 0; + struct rr_data_txt *txt_rec; + int parse_error = 0; + + assert(pkt != NULL); + + if (off > pkt_len) + return 0; + + rr = malloc(sizeof(struct rr_entry)); + memset(rr, 0, sizeof(struct rr_entry)); + + name = uncompress_nlabel(pkt_buf, pkt_len, off); + p += label_len(pkt_buf, pkt_len, off); + rr->name = name; + + rr->type = mdns_read_u16(p); + p += sizeof(uint16_t); + + rr->cache_flush = (*p & 0x80) == 0x80; + rr->rr_class = mdns_read_u16(p) & ~0x80; + p += sizeof(uint16_t); + + rr->ttl = mdns_read_u32(p); + p += sizeof(uint32_t); + + // RR data + rr_data_len = mdns_read_u16(p); + p += sizeof(uint16_t); + + if (p + rr_data_len > e) { + DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len, e - p); + rr_entry_destroy(rr); + return 0; + } + + e = p + rr_data_len; + + // see if we can parse the RR data + switch (rr->type) { + case RR_A: + if (rr_data_len < sizeof(uint32_t)) { + DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len); + parse_error = 1; + break; + } + rr->data.A.addr = ntohl(mdns_read_u32(p)); /* addr already in net order */ + p += sizeof(uint32_t); + break; + + case RR_AAAA: + if (rr_data_len < sizeof(struct in6_addr)) { + DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len); + parse_error = 1; + break; + } + rr->data.AAAA.addr = malloc(sizeof(struct in6_addr)); + int i; + for (i = 0; i < sizeof(struct in6_addr); i++) + rr->data.AAAA.addr->s6_addr[i] = p[i]; + p += sizeof(struct in6_addr); + break; + + case RR_PTR: + rr->data.PTR.name = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf); + if (rr->data.PTR.name == NULL) { + DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n"); + parse_error = 1; + break; + } + p += rr_data_len; + break; + + case RR_TXT: + txt_rec = &rr->data.TXT; + + // not supposed to happen, but we should handle it + if (rr_data_len == 0) { + DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n"); + txt_rec->txt = create_label(""); + break; + } + + while (1) { + txt_rec->txt = copy_label(pkt_buf, pkt_len, p - pkt_buf); + if (txt_rec->txt == NULL) { + DEBUG_PRINTF("unable to copy label for TXT record\n"); + parse_error = 1; + break; + } + p += txt_rec->txt[0] + 1; + + if (p >= e) + break; + + // allocate another record + txt_rec->next = malloc(sizeof(struct rr_data_txt)); + txt_rec = txt_rec->next; + txt_rec->next = NULL; + } + break; + + default: + // skip to end of RR data + p = e; + } + + // if there was a parse error, destroy partial rr_entry + if (parse_error) { + rr_entry_destroy(rr); + return 0; + } + + rr_list_append(&pkt->rr_ans, rr); + + return p - (pkt_buf + off); } // parse a MDNS packet into an mdns_pkt struct struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len) { - uint8_t *p = pkt_buf; - size_t off; - struct mdns_pkt *pkt; - int i; - - if (pkt_len < 12) - return NULL; - - MALLOC_ZERO_STRUCT(pkt, mdns_pkt); - - // parse header - pkt->id = mdns_read_u16(p); p += sizeof(uint16_t); - pkt->flags = mdns_read_u16(p); p += sizeof(uint16_t); - pkt->num_qn = mdns_read_u16(p); p += sizeof(uint16_t); - pkt->num_ans_rr = mdns_read_u16(p); p += sizeof(uint16_t); - pkt->num_auth_rr = mdns_read_u16(p); p += sizeof(uint16_t); - pkt->num_add_rr = mdns_read_u16(p); p += sizeof(uint16_t); - - off = p - pkt_buf; - - // parse questions - for (i = 0; i < pkt->num_qn; i++) { - size_t l = mdns_parse_qn(pkt_buf, pkt_len, off, pkt); - if (! l) { - DEBUG_PRINTF("error parsing question #%d\n", i); - mdns_pkt_destroy(pkt); - return NULL; - } - - off += l; + uint8_t *p = pkt_buf; + size_t off; + struct mdns_pkt *pkt; + int i; + + if (pkt_len < 12) + return NULL; + + MALLOC_ZERO_STRUCT(pkt, mdns_pkt); + + // parse header + pkt->id = mdns_read_u16(p); + p += sizeof(uint16_t); + pkt->flags = mdns_read_u16(p); + p += sizeof(uint16_t); + pkt->num_qn = mdns_read_u16(p); + p += sizeof(uint16_t); + pkt->num_ans_rr = mdns_read_u16(p); + p += sizeof(uint16_t); + pkt->num_auth_rr = mdns_read_u16(p); + p += sizeof(uint16_t); + pkt->num_add_rr = mdns_read_u16(p); + p += sizeof(uint16_t); + + off = p - pkt_buf; + + // parse questions + for (i = 0; i < pkt->num_qn; i++) { + size_t l = mdns_parse_qn(pkt_buf, pkt_len, off, pkt); + if (!l) { + DEBUG_PRINTF("error parsing question #%d\n", i); + mdns_pkt_destroy(pkt); + return NULL; + } + + off += l; + } + + // parse answer RRs + for (i = 0; i < pkt->num_ans_rr; i++) { + size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt); + if (!l) { + DEBUG_PRINTF("error parsing answer #%d\n", i); + mdns_pkt_destroy(pkt); + return NULL; + } + + off += l; + } + + // TODO: parse the authority and additional RR sections + + return pkt; +} + +// encodes a name (label) into a packet using the name compression scheme +// encoded names will be added to the compression list for subsequent use +static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off, const uint8_t *name, + struct name_comp *comp) { + struct name_comp *c, *c_tail = NULL; + uint8_t *p = pkt_buf + off; + size_t len = 0; + + if (name) { + while (*name) { + // find match for compression + for (c = comp; c; c = c->next) { + if (cmp_nlabel(name, c->label) == 0) { + mdns_write_u16(p, 0xC000 | (c->pos & ~0xC000)); + return len + sizeof(uint16_t); } - // parse answer RRs - for (i = 0; i < pkt->num_ans_rr; i++) { - size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt); - if (! l) { - DEBUG_PRINTF("error parsing answer #%d\n", i); - mdns_pkt_destroy(pkt); - return NULL; - } + if (c->next == NULL) + c_tail = c; + } - off += l; - } + // copy this segment + int segment_len = *name + 1; + strncpy((char *)p, (char *)name, segment_len); - // TODO: parse the authority and additional RR sections + // cache the name for subsequent compression + DECL_MALLOC_ZERO_STRUCT(new_c, name_comp); - return pkt; -} + new_c->label = (uint8_t *)name; + new_c->pos = p - pkt_buf; + c_tail->next = new_c; -// encodes a name (label) into a packet using the name compression scheme -// encoded names will be added to the compression list for subsequent use -static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off, - const uint8_t *name, struct name_comp *comp) { - struct name_comp *c, *c_tail = NULL; - uint8_t *p = pkt_buf + off; - size_t len = 0; - - if (name) { - while (*name) { - // find match for compression - for (c = comp; c; c = c->next) { - if (cmp_nlabel(name, c->label) == 0) { - mdns_write_u16(p, 0xC000 | (c->pos & ~0xC000)); - return len + sizeof(uint16_t); - } - - if (c->next == NULL) - c_tail = c; - } - - // copy this segment - int segment_len = *name + 1; - strncpy((char *) p, (char *) name, segment_len); - - // cache the name for subsequent compression - DECL_MALLOC_ZERO_STRUCT(new_c, name_comp); - - new_c->label = (uint8_t *) name; - new_c->pos = p - pkt_buf; - c_tail->next = new_c; - - // advance to next name segment - p += segment_len; - len += segment_len; - name += segment_len; - } - } + // advance to next name segment + p += segment_len; + len += segment_len; + name += segment_len; + } + } - *p = '\0'; // root "label" - len += 1; + *p = '\0'; // root "label" + len += 1; - return len; + return len; } // encodes an RR entry at the given offset // returns the size of the entire RR entry -static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, - struct rr_entry *rr, struct name_comp *comp) { - uint8_t *p = pkt_buf + off, *p_data; - size_t l; - struct rr_data_txt *txt_rec; - uint8_t *label; - int i; +static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct rr_entry *rr, + struct name_comp *comp) { + uint8_t *p = pkt_buf + off, *p_data; + size_t l; + struct rr_data_txt *txt_rec; + uint8_t *label; + int i; - assert(off < pkt_len); + assert(off < pkt_len); - // name - l = mdns_encode_name(pkt_buf, pkt_len, off, rr->name, comp); - assert(l != 0); - p += l; + // name + l = mdns_encode_name(pkt_buf, pkt_len, off, rr->name, comp); + assert(l != 0); + p += l; - // type - p = mdns_write_u16(p, rr->type); + // type + p = mdns_write_u16(p, rr->type); - // class & cache flush - p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15)); + // class & cache flush + p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15)); - // TTL - p = mdns_write_u32(p, rr->ttl); + // TTL + p = mdns_write_u32(p, rr->ttl); - // data length (filled in later) - p += sizeof(uint16_t); + // data length (filled in later) + p += sizeof(uint16_t); - // start of data marker - p_data = p; + // start of data marker + p_data = p; - switch (rr->type) { - case RR_A: - /* htonl() needed coz addr already in net order */ - p = mdns_write_u32(p, htonl(rr->data.A.addr)); - break; + switch (rr->type) { + case RR_A: + /* htonl() needed coz addr already in net order */ + p = mdns_write_u32(p, htonl(rr->data.A.addr)); + break; - case RR_AAAA: - for (i = 0; i < sizeof(struct in6_addr); i++) - *p++ = rr->data.AAAA.addr->s6_addr[i]; - break; + case RR_AAAA: + for (i = 0; i < sizeof(struct in6_addr); i++) + *p++ = rr->data.AAAA.addr->s6_addr[i]; + break; - case RR_PTR: - label = rr->data.PTR.name ? - rr->data.PTR.name : - rr->data.PTR.entry->name; - p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp); - break; + case RR_PTR: + label = rr->data.PTR.name ? rr->data.PTR.name : rr->data.PTR.entry->name; + p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp); + break; - case RR_TXT: - txt_rec = &rr->data.TXT; - for (; txt_rec; txt_rec = txt_rec->next) { - int len = txt_rec->txt[0] + 1; - strncpy((char *) p, (char *) txt_rec->txt, len); - p += len; - } - break; + case RR_TXT: + txt_rec = &rr->data.TXT; + for (; txt_rec; txt_rec = txt_rec->next) { + int len = txt_rec->txt[0] + 1; + strncpy((char *)p, (char *)txt_rec->txt, len); + p += len; + } + break; - case RR_SRV: - p = mdns_write_u16(p, rr->data.SRV.priority); + case RR_SRV: + p = mdns_write_u16(p, rr->data.SRV.priority); - p = mdns_write_u16(p, rr->data.SRV.weight); + p = mdns_write_u16(p, rr->data.SRV.weight); - p = mdns_write_u16(p, rr->data.SRV.port); + p = mdns_write_u16(p, rr->data.SRV.port); - p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, - rr->data.SRV.target, comp); - break; + p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, rr->data.SRV.target, comp); + break; - case RR_NSEC: - p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, - rr->name, comp); + case RR_NSEC: + p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, rr->name, comp); - *p++ = 0; // bitmap window/block number + *p++ = 0; // bitmap window/block number - *p++ = sizeof(rr->data.NSEC.bitmap); // bitmap length + *p++ = sizeof(rr->data.NSEC.bitmap); // bitmap length - for (i = 0; i < sizeof(rr->data.NSEC.bitmap); i++) - *p++ = rr->data.NSEC.bitmap[i]; + for (i = 0; i < sizeof(rr->data.NSEC.bitmap); i++) + *p++ = rr->data.NSEC.bitmap[i]; - break; + break; - default: - DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr->type); - } + default: + DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr->type); + } - // calculate data length based on p - l = p - p_data; + // calculate data length based on p + l = p - p_data; - // fill in the length - mdns_write_u16(p - l - sizeof(uint16_t), l); + // fill in the length + mdns_write_u16(p - l - sizeof(uint16_t), l); - return p - pkt_buf - off; + return p - pkt_buf - off; } // encodes a MDNS packet from the given mdns_pkt struct into a buffer // returns the size of the entire MDNS packet size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len) { - struct name_comp *comp; - uint8_t *p = pkt_buf; - //uint8_t *e = pkt_buf + pkt_len; - size_t off; - int i; - - assert(answer != NULL); - assert(pkt_len >= 12); - - if (p == NULL) - return -1; - - // this is an Answer - number of qns should be zero - assert(answer->num_qn == 0); - - p = mdns_write_u16(p, answer->id); - p = mdns_write_u16(p, answer->flags); - p = mdns_write_u16(p, answer->num_qn); - p = mdns_write_u16(p, answer->num_ans_rr); - p = mdns_write_u16(p, answer->num_auth_rr); - p = mdns_write_u16(p, answer->num_add_rr); - - off = p - pkt_buf; - - // allocate list for name compression - comp = malloc(sizeof(struct name_comp)); - if (comp == NULL) - return -1; - memset(comp, 0, sizeof(struct name_comp)); - - // dummy entry - comp->label = (uint8_t *) ""; - comp->pos = 0; - - // skip encoding of qn - - struct rr_list *rr_set[] = { - answer->rr_ans, - answer->rr_auth, - answer->rr_add - }; - - // encode answer, authority and additional RRs - for (i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) { - struct rr_list *rr = rr_set[i]; - for (; rr; rr = rr->next) { - size_t l = mdns_encode_rr(pkt_buf, pkt_len, off, rr->e, comp); - off += l; - - if (off >= pkt_len) { - DEBUG_PRINTF("packet buffer too small\n"); - return -1; - } - } - - } - - // free name compression list - while (comp) { - struct name_comp *c = comp->next; - free(comp); - comp = c; - } - - return off; + struct name_comp *comp; + uint8_t *p = pkt_buf; + // uint8_t *e = pkt_buf + pkt_len; + size_t off; + int i; + + assert(answer != NULL); + assert(pkt_len >= 12); + + if (p == NULL) + return -1; + + // this is an Answer - number of qns should be zero + assert(answer->num_qn == 0); + + p = mdns_write_u16(p, answer->id); + p = mdns_write_u16(p, answer->flags); + p = mdns_write_u16(p, answer->num_qn); + p = mdns_write_u16(p, answer->num_ans_rr); + p = mdns_write_u16(p, answer->num_auth_rr); + p = mdns_write_u16(p, answer->num_add_rr); + + off = p - pkt_buf; + + // allocate list for name compression + comp = malloc(sizeof(struct name_comp)); + if (comp == NULL) + return -1; + memset(comp, 0, sizeof(struct name_comp)); + + // dummy entry + comp->label = (uint8_t *)""; + comp->pos = 0; + + // skip encoding of qn + + struct rr_list *rr_set[] = {answer->rr_ans, answer->rr_auth, answer->rr_add}; + + // encode answer, authority and additional RRs + for (i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) { + struct rr_list *rr = rr_set[i]; + for (; rr; rr = rr->next) { + size_t l = mdns_encode_rr(pkt_buf, pkt_len, off, rr->e, comp); + off += l; + + if (off >= pkt_len) { + DEBUG_PRINTF("packet buffer too small\n"); + return -1; + } + } + } + + // free name compression list + while (comp) { + struct name_comp *c = comp->next; + free(comp); + comp = c; + } + + return off; } //******************************************************// @@ -1064,621 +1063,599 @@ size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len #define PACKET_SIZE 65536 -#define SERVICES_DNS_SD_NLABEL \ - ((uint8_t *) "\x09_services\x07_dns-sd\x04_udp\x05local") +#define SERVICES_DNS_SD_NLABEL ((uint8_t *)"\x09_services\x07_dns-sd\x04_udp\x05local") struct mdnsd { - pthread_mutex_t data_lock; - int sockfd; - int notify_pipe[2]; - int stop_flag; - - struct rr_group *group; - struct rr_list *announce; - struct rr_list *services; - uint8_t *hostname; + pthread_mutex_t data_lock; + int sockfd; + int notify_pipe[2]; + int stop_flag; + + struct rr_group *group; + struct rr_list *announce; + struct rr_list *services; + uint8_t *hostname; }; struct mdns_service { - struct rr_list *entries; + struct rr_list *entries; }; ///////////////////////////////// - - static int create_recv_sock() { - int sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) { - log_message(LOG_ERR, "recv socket(): %m"); - return sd; - } - - int r = -1; - - int on = 1; - if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m"); - return r; - } - - /* bind to an address */ - struct sockaddr_in serveraddr; - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - serveraddr.sin_port = htons(MDNS_PORT); - serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */ - if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) { - log_message(LOG_ERR, "recv bind(): %m"); - } - - // add membership to receiving socket - struct ip_mreq mreq; - memset(&mreq, 0, sizeof(struct ip_mreq)); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); - if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m"); - return r; - } - - // enable loopback in case someone else needs the data - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m"); - return r; - } - + int sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) { + log_message(LOG_ERR, "recv socket(): %m"); + return sd; + } + + int r = -1; + + int on = 1; + if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0) { + log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m"); + return r; + } + + /* bind to an address */ + struct sockaddr_in serveraddr; + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_port = htons(MDNS_PORT); + serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */ + if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) { + log_message(LOG_ERR, "recv bind(): %m"); + } + + // add membership to receiving socket + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(struct ip_mreq)); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); + if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq))) < 0) { + log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m"); + return r; + } + + // enable loopback in case someone else needs the data + if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&on, sizeof(on))) < 0) { + log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m"); + return r; + } #ifdef IP_PKTINFO - if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *) &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m"); - return r; - } + if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *)&on, sizeof(on))) < 0) { + log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m"); + return r; + } #endif - return sd; + return sd; } static ssize_t send_packet(int fd, const void *data, size_t len) { - static struct sockaddr_in toaddr; - if (toaddr.sin_family != AF_INET) { - memset(&toaddr, 0, sizeof(struct sockaddr_in)); - toaddr.sin_family = AF_INET; - toaddr.sin_port = htons(MDNS_PORT); - toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR); - } - - return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); + static struct sockaddr_in toaddr; + if (toaddr.sin_family != AF_INET) { + memset(&toaddr, 0, sizeof(struct sockaddr_in)); + toaddr.sin_family = AF_INET; + toaddr.sin_port = htons(MDNS_PORT); + toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR); + } + + return sendto(fd, data, len, 0, (struct sockaddr *)&toaddr, sizeof(struct sockaddr_in)); } - // populate the specified list which matches the RR name and type // type can be RR_ANY, which populates all entries EXCEPT RR_NSEC -static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) { - int num_ans = 0; - - // check if we have the records - pthread_mutex_lock(&svr->data_lock); - struct rr_group *ans_grp = rr_group_find(svr->group, name); - if (ans_grp == NULL) { - pthread_mutex_unlock(&svr->data_lock); - return num_ans; - } - - // decide which records should go into answers - struct rr_list *n = ans_grp->rr; - for (; n; n = n->next) { - // exclude NSEC for RR_ANY - if (type == RR_ANY && n->e->type == RR_NSEC) - continue; - - if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) { - num_ans += rr_list_append(rr_head, n->e); - } - } - - pthread_mutex_unlock(&svr->data_lock); - - return num_ans; +static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, + enum rr_type type) { + int num_ans = 0; + + // check if we have the records + pthread_mutex_lock(&svr->data_lock); + struct rr_group *ans_grp = rr_group_find(svr->group, name); + if (ans_grp == NULL) { + pthread_mutex_unlock(&svr->data_lock); + return num_ans; + } + + // decide which records should go into answers + struct rr_list *n = ans_grp->rr; + for (; n; n = n->next) { + // exclude NSEC for RR_ANY + if (type == RR_ANY && n->e->type == RR_NSEC) + continue; + + if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) { + num_ans += rr_list_append(rr_head, n->e); + } + } + + pthread_mutex_unlock(&svr->data_lock); + + return num_ans; } // given a list of RRs, look up related records and add them static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_pkt *reply) { - for (; list; list = list->next) { - struct rr_entry *ans = list->e; - - switch (ans->type) { - case RR_PTR: - // target host A, AAAA records - reply->num_add_rr += populate_answers(svr, &reply->rr_add, - MDNS_RR_GET_PTR_NAME(ans), RR_ANY); - break; - - case RR_SRV: - // target host A, AAAA records - reply->num_add_rr += populate_answers(svr, &reply->rr_add, - ans->data.SRV.target, RR_ANY); - - // perhaps TXT records of the same name? - // if we use RR_ANY, we risk pulling in the same RR_SRV - reply->num_add_rr += populate_answers(svr, &reply->rr_add, - ans->name, RR_TXT); - break; - - case RR_A: - case RR_AAAA: - reply->num_add_rr += populate_answers(svr, &reply->rr_add, - ans->name, RR_NSEC); - break; - - default: - // nothing to add - break; - } - } + for (; list; list = list->next) { + struct rr_entry *ans = list->e; + + switch (ans->type) { + case RR_PTR: + // target host A, AAAA records + reply->num_add_rr += populate_answers(svr, &reply->rr_add, MDNS_RR_GET_PTR_NAME(ans), RR_ANY); + break; + + case RR_SRV: + // target host A, AAAA records + reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->data.SRV.target, RR_ANY); + + // perhaps TXT records of the same name? + // if we use RR_ANY, we risk pulling in the same RR_SRV + reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->name, RR_TXT); + break; + + case RR_A: + case RR_AAAA: + reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->name, RR_NSEC); + break; + + default: + // nothing to add + break; + } + } } // creates an announce packet given the type name PTR static void announce_srv(struct mdnsd *svr, struct mdns_pkt *reply, uint8_t *name) { - mdns_init_reply(reply, 0); + mdns_init_reply(reply, 0); - reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR); + reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR); - // remember to add the services dns-sd PTR too - reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, - SERVICES_DNS_SD_NLABEL, RR_PTR); + // remember to add the services dns-sd PTR too + reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, SERVICES_DNS_SD_NLABEL, RR_PTR); - // see if we can match additional records for answers - add_related_rr(svr, reply->rr_ans, reply); + // see if we can match additional records for answers + add_related_rr(svr, reply->rr_ans, reply); - // additional records for additional records - add_related_rr(svr, reply->rr_add, reply); + // additional records for additional records + add_related_rr(svr, reply->rr_add, reply); } // processes the incoming MDNS packet // returns >0 if processed, 0 otherwise static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns_pkt *reply) { - int i; - - assert(pkt != NULL); + int i; - // is it standard query? - if ((pkt->flags & MDNS_FLAG_RESP) == 0 && - MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) { - mdns_init_reply(reply, pkt->id); + assert(pkt != NULL); - DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n", - pkt->flags, - pkt->num_qn, - pkt->num_ans_rr, - pkt->num_add_rr); + // is it standard query? + if ((pkt->flags & MDNS_FLAG_RESP) == 0 && MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) { + mdns_init_reply(reply, pkt->id); - // loop through questions - struct rr_list *qnl = pkt->rr_qn; - for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) { - struct rr_entry *qn = qnl->e; - int num_ans_added = 0; + DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n", pkt->flags, pkt->num_qn, + pkt->num_ans_rr, pkt->num_add_rr); - char *namestr = nlabel_to_str(qn->name); - DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr); - free(namestr); + // loop through questions + struct rr_list *qnl = pkt->rr_qn; + for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) { + struct rr_entry *qn = qnl->e; + int num_ans_added = 0; - // check if it's a unicast query - we ignore those - if (qn->unicast_query) { - DEBUG_PRINTF("skipping unicast query\n"); - continue; - } + char *namestr = nlabel_to_str(qn->name); + DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, + namestr); + free(namestr); - num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type); - reply->num_ans_rr += num_ans_added; + // check if it's a unicast query - we ignore those + if (qn->unicast_query) { + DEBUG_PRINTF("skipping unicast query\n"); + continue; + } - DEBUG_PRINTF("added %d answers\n", num_ans_added); - } + num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type); + reply->num_ans_rr += num_ans_added; - // remove our replies if they were already in their answers - struct rr_list *ans = NULL, *prev_ans = NULL; - for (ans = reply->rr_ans; ans; ) { - struct rr_list *next_ans = ans->next; - struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e); + DEBUG_PRINTF("added %d answers\n", num_ans_added); + } - // discard answers that have at least half of the actual TTL - if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) { - char *namestr = nlabel_to_str(ans->e->name); - DEBUG_PRINTF("removing answer for %s\n", namestr); - free(namestr); + // remove our replies if they were already in their answers + struct rr_list *ans = NULL, *prev_ans = NULL; + for (ans = reply->rr_ans; ans;) { + struct rr_list *next_ans = ans->next; + struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e); - // check if list item is head - if (prev_ans == NULL) - reply->rr_ans = ans->next; - else - prev_ans->next = ans->next; - free(ans); + // discard answers that have at least half of the actual TTL + if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) { + char *namestr = nlabel_to_str(ans->e->name); + DEBUG_PRINTF("removing answer for %s\n", namestr); + free(namestr); - ans = prev_ans; + // check if list item is head + if (prev_ans == NULL) + reply->rr_ans = ans->next; + else + prev_ans->next = ans->next; + free(ans); - // adjust answer count - reply->num_ans_rr--; - } + ans = prev_ans; - prev_ans = ans; - ans = next_ans; - } + // adjust answer count + reply->num_ans_rr--; + } + prev_ans = ans; + ans = next_ans; + } - // see if we can match additional records for answers - add_related_rr(svr, reply->rr_ans, reply); + // see if we can match additional records for answers + add_related_rr(svr, reply->rr_ans, reply); - // additional records for additional records - add_related_rr(svr, reply->rr_add, reply); + // additional records for additional records + add_related_rr(svr, reply->rr_add, reply); - DEBUG_PRINTF("\n"); + DEBUG_PRINTF("\n"); - return reply->num_ans_rr; - } + return reply->num_ans_rr; + } - return 0; + return 0; } int create_pipe(int handles[2]) { #ifdef _WIN32 - SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock == INVALID_SOCKET) { - return -1; - } - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(0); - serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - if (listen(sock, 1) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - int len = sizeof(serv_addr); - if (getsockname(sock, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - closesocket(sock); - return -1; - } - if (connect(handles[1], (struct sockaddr*)&serv_addr, len) == SOCKET_ERROR) { - closesocket(sock); - return -1; - } - if ((handles[0] = accept(sock, (struct sockaddr*)&serv_addr, &len)) == INVALID_SOCKET) { - closesocket((SOCKET)handles[1]); - handles[1] = INVALID_SOCKET; - closesocket(sock); - return -1; - } - closesocket(sock); - return 0; + SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + return -1; + } + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(0); + serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + if (listen(sock, 1) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + int len = sizeof(serv_addr); + if (getsockname(sock, (SOCKADDR *)&serv_addr, &len) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + closesocket(sock); + return -1; + } + if (connect(handles[1], (struct sockaddr *)&serv_addr, len) == SOCKET_ERROR) { + closesocket(sock); + return -1; + } + if ((handles[0] = accept(sock, (struct sockaddr *)&serv_addr, &len)) == INVALID_SOCKET) { + closesocket((SOCKET)handles[1]); + handles[1] = INVALID_SOCKET; + closesocket(sock); + return -1; + } + closesocket(sock); + return 0; #else - return pipe(handles); + return pipe(handles); #endif } -int read_pipe(int s, char* buf, int len) { +int read_pipe(int s, char *buf, int len) { #ifdef _WIN32 - int ret = recv(s, buf, len, 0); - if (ret < 0 && WSAGetLastError() == WSAECONNRESET) { - ret = 0; - } - return ret; + int ret = recv(s, buf, len, 0); + if (ret < 0 && WSAGetLastError() == WSAECONNRESET) { + ret = 0; + } + return ret; #else - return read(s, buf, len); + return read(s, buf, len); #endif } -int write_pipe(int s, char* buf, int len) { +int write_pipe(int s, char *buf, int len) { #ifdef _WIN32 - return send(s, buf, len, 0); + return send(s, buf, len, 0); #else - return write(s, buf, len); + return write(s, buf, len); #endif } int close_pipe(int s) { #ifdef _WIN32 - return closesocket(s); + return closesocket(s); #else - return close(s); + return close(s); #endif } // main loop to receive, process and send out MDNS replies // also handles MDNS service announces static void main_loop(struct mdnsd *svr) { - fd_set sockfd_set; - int max_fd = svr->sockfd; - char notify_buf[2]; // buffer for reading of notify_pipe - - void *pkt_buffer = malloc(PACKET_SIZE); - - if (svr->notify_pipe[0] > max_fd) - max_fd = svr->notify_pipe[0]; - - struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt)); - memset(mdns_reply, 0, sizeof(struct mdns_pkt)); - - while (! svr->stop_flag) { - FD_ZERO(&sockfd_set); - FD_SET(svr->sockfd, &sockfd_set); - FD_SET(svr->notify_pipe[0], &sockfd_set); - select(max_fd + 1, &sockfd_set, NULL, NULL, NULL); - - if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) { - // flush the notify_pipe - read_pipe(svr->notify_pipe[0], (char*)¬ify_buf, 1); - } else if (FD_ISSET(svr->sockfd, &sockfd_set)) { - struct sockaddr_in fromaddr; - socklen_t sockaddr_size = sizeof(struct sockaddr_in); - - ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0, - (struct sockaddr *) &fromaddr, &sockaddr_size); - if (recvsize < 0) { - log_message(LOG_ERR, "recv(): %m"); - } - - DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize); - struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize); - if (mdns != NULL) { - if (process_mdns_pkt(svr, mdns, mdns_reply)) { - size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); - send_packet(svr->sockfd, pkt_buffer, replylen); - } else if (mdns->num_qn == 0) { - DEBUG_PRINTF("(no questions in packet)\n\n"); - } - - mdns_pkt_destroy(mdns); - } - } - - // send out announces - while (1) { - struct rr_entry *ann_e = NULL; - - // extract from head of list - pthread_mutex_lock(&svr->data_lock); - if (svr->announce) - ann_e = rr_list_remove(&svr->announce, svr->announce->e); - pthread_mutex_unlock(&svr->data_lock); - - if (! ann_e) - break; - - char *namestr = nlabel_to_str(ann_e->name); - DEBUG_PRINTF("sending announce for %s\n", namestr); - free(namestr); - - announce_srv(svr, mdns_reply, ann_e->name); - - if (mdns_reply->num_ans_rr > 0) { - size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); - send_packet(svr->sockfd, pkt_buffer, replylen); - } - } + fd_set sockfd_set; + int max_fd = svr->sockfd; + char notify_buf[2]; // buffer for reading of notify_pipe + + void *pkt_buffer = malloc(PACKET_SIZE); + + if (svr->notify_pipe[0] > max_fd) + max_fd = svr->notify_pipe[0]; + + struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt)); + memset(mdns_reply, 0, sizeof(struct mdns_pkt)); + + while (!svr->stop_flag) { + FD_ZERO(&sockfd_set); + FD_SET(svr->sockfd, &sockfd_set); + FD_SET(svr->notify_pipe[0], &sockfd_set); + select(max_fd + 1, &sockfd_set, NULL, NULL, NULL); + + if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) { + // flush the notify_pipe + read_pipe(svr->notify_pipe[0], (char *)¬ify_buf, 1); + } else if (FD_ISSET(svr->sockfd, &sockfd_set)) { + struct sockaddr_in fromaddr; + socklen_t sockaddr_size = sizeof(struct sockaddr_in); + + ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0, + (struct sockaddr *)&fromaddr, &sockaddr_size); + if (recvsize < 0) { + log_message(LOG_ERR, "recv(): %m"); + } + + DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long)recvsize); + struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize); + if (mdns != NULL) { + if (process_mdns_pkt(svr, mdns, mdns_reply)) { + size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); + send_packet(svr->sockfd, pkt_buffer, replylen); + } else if (mdns->num_qn == 0) { + DEBUG_PRINTF("(no questions in packet)\n\n"); } - // main thread terminating. send out "goodbye packets" for services - mdns_init_reply(mdns_reply, 0); + mdns_pkt_destroy(mdns); + } + } - pthread_mutex_lock(&svr->data_lock); - struct rr_list *svc_le = svr->services; - for (; svc_le; svc_le = svc_le->next) { - // set TTL to zero - svc_le->e->ttl = 0; - mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e); - } - pthread_mutex_unlock(&svr->data_lock); + // send out announces + while (1) { + struct rr_entry *ann_e = NULL; - // send out packet - if (mdns_reply->num_ans_rr > 0) { - size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); - send_packet(svr->sockfd, pkt_buffer, replylen); - } + // extract from head of list + pthread_mutex_lock(&svr->data_lock); + if (svr->announce) + ann_e = rr_list_remove(&svr->announce, svr->announce->e); + pthread_mutex_unlock(&svr->data_lock); - // destroy packet - mdns_init_reply(mdns_reply, 0); - free(mdns_reply); + if (!ann_e) + break; - free(pkt_buffer); + char *namestr = nlabel_to_str(ann_e->name); + DEBUG_PRINTF("sending announce for %s\n", namestr); + free(namestr); - close_pipe(svr->sockfd); + announce_srv(svr, mdns_reply, ann_e->name); - svr->stop_flag = 2; -} + if (mdns_reply->num_ans_rr > 0) { + size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); + send_packet(svr->sockfd, pkt_buffer, replylen); + } + } + } -///////////////////////////////////////////////////// + // main thread terminating. send out "goodbye packets" for services + mdns_init_reply(mdns_reply, 0); + pthread_mutex_lock(&svr->data_lock); + struct rr_list *svc_le = svr->services; + for (; svc_le; svc_le = svc_le->next) { + // set TTL to zero + svc_le->e->ttl = 0; + mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e); + } + pthread_mutex_unlock(&svr->data_lock); -void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) { - struct rr_entry *a_e = NULL, - *nsec_e = NULL; + // send out packet + if (mdns_reply->num_ans_rr > 0) { + size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE); + send_packet(svr->sockfd, pkt_buffer, replylen); + } - // currently can't be called twice - // dont ask me what happens if the IP changes - assert(svr->hostname == NULL); + // destroy packet + mdns_init_reply(mdns_reply, 0); + free(mdns_reply); - a_e = rr_create_a(create_nlabel(hostname), ip); // 120 seconds automatically + free(pkt_buffer); - nsec_e = rr_create(create_nlabel(hostname), RR_NSEC); - nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500) - rr_set_nsec(nsec_e, RR_A); + close_pipe(svr->sockfd); - pthread_mutex_lock(&svr->data_lock); - svr->hostname = create_nlabel(hostname); - rr_group_add(&svr->group, a_e); - rr_group_add(&svr->group, nsec_e); - pthread_mutex_unlock(&svr->data_lock); + svr->stop_flag = 2; } -void mdnsd_set_hostname_v6(struct mdnsd *svr, const char *hostname, struct in6_addr *addr) -{ - struct rr_entry *aaaa_e = NULL, - *nsec_e = NULL; +///////////////////////////////////////////////////// - // currently can't be called twice - // dont ask me what happens if the IP changes - assert(svr->hostname == NULL); +void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) { + struct rr_entry *a_e = NULL, *nsec_e = NULL; - aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // 120 seconds automatically + // currently can't be called twice + // dont ask me what happens if the IP changes + assert(svr->hostname == NULL); - nsec_e = rr_create(create_nlabel(hostname), RR_NSEC); - nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500) - rr_set_nsec(nsec_e, RR_AAAA); + a_e = rr_create_a(create_nlabel(hostname), ip); // 120 seconds automatically - pthread_mutex_lock(&svr->data_lock); - svr->hostname = create_nlabel(hostname); - rr_group_add(&svr->group, aaaa_e); - rr_group_add(&svr->group, nsec_e); - pthread_mutex_unlock(&svr->data_lock); -} + nsec_e = rr_create(create_nlabel(hostname), RR_NSEC); + nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500) + rr_set_nsec(nsec_e, RR_A); -void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) { - pthread_mutex_lock(&svr->data_lock); - rr_group_add(&svr->group, rr); - pthread_mutex_unlock(&svr->data_lock); + pthread_mutex_lock(&svr->data_lock); + svr->hostname = create_nlabel(hostname); + rr_group_add(&svr->group, a_e); + rr_group_add(&svr->group, nsec_e); + pthread_mutex_unlock(&svr->data_lock); } -struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name, - const char *type, uint16_t port, const char *hostname, const char *txt[]) { - struct rr_entry *txt_e = NULL, - *srv_e = NULL, - *ptr_e = NULL, - *bptr_e = NULL; - uint8_t *target; - uint8_t *inst_nlabel, *type_nlabel, *nlabel; - struct mdns_service *service = malloc(sizeof(struct mdns_service)); - memset(service, 0, sizeof(struct mdns_service)); - - // combine service name - type_nlabel = create_nlabel(type); - inst_nlabel = create_label(instance_name); - nlabel = join_nlabel(inst_nlabel, type_nlabel); - - // create TXT record - if (txt && *txt) { - txt_e = rr_create(dup_nlabel(nlabel), RR_TXT); // automatically 4500 seconds - rr_list_append(&service->entries, txt_e); - - // add TXTs - for (; *txt; txt++) - rr_add_txt(txt_e, *txt); - } - - // create SRV record - assert(hostname || svr->hostname); // either one as target - target = hostname ? - create_nlabel(hostname) : - dup_nlabel(svr->hostname); - - srv_e = rr_create_srv(dup_nlabel(nlabel), port, target); // automatically 4500 seconds - rr_list_append(&service->entries, srv_e); - - // create PTR record for type - ptr_e = rr_create_ptr(type_nlabel, srv_e); // automatically 4500 seconds - - // create services PTR record for type - // this enables the type to show up as a "service" - bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e); // automatically 4500 seconds +void mdnsd_set_hostname_v6(struct mdnsd *svr, const char *hostname, struct in6_addr *addr) { + struct rr_entry *aaaa_e = NULL, *nsec_e = NULL; - // modify lists here - pthread_mutex_lock(&svr->data_lock); + // currently can't be called twice + // dont ask me what happens if the IP changes + assert(svr->hostname == NULL); - if (txt_e) - rr_group_add(&svr->group, txt_e); - rr_group_add(&svr->group, srv_e); - rr_group_add(&svr->group, ptr_e); - rr_group_add(&svr->group, bptr_e); + aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // 120 seconds automatically - // append PTR entry to announce list - rr_list_append(&svr->announce, ptr_e); - rr_list_append(&svr->services, ptr_e); + nsec_e = rr_create(create_nlabel(hostname), RR_NSEC); + nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500) + rr_set_nsec(nsec_e, RR_AAAA); - pthread_mutex_unlock(&svr->data_lock); - - // don't free type_nlabel - it's with the PTR record - free(nlabel); - free(inst_nlabel); + pthread_mutex_lock(&svr->data_lock); + svr->hostname = create_nlabel(hostname); + rr_group_add(&svr->group, aaaa_e); + rr_group_add(&svr->group, nsec_e); + pthread_mutex_unlock(&svr->data_lock); +} - // notify server - write_pipe(svr->notify_pipe[1], ".", 1); +void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) { + pthread_mutex_lock(&svr->data_lock); + rr_group_add(&svr->group, rr); + pthread_mutex_unlock(&svr->data_lock); +} - return service; +struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name, + const char *type, uint16_t port, const char *hostname, + const char *txt[]) { + struct rr_entry *txt_e = NULL, *srv_e = NULL, *ptr_e = NULL, *bptr_e = NULL; + uint8_t *target; + uint8_t *inst_nlabel, *type_nlabel, *nlabel; + struct mdns_service *service = malloc(sizeof(struct mdns_service)); + memset(service, 0, sizeof(struct mdns_service)); + + // combine service name + type_nlabel = create_nlabel(type); + inst_nlabel = create_label(instance_name); + nlabel = join_nlabel(inst_nlabel, type_nlabel); + + // create TXT record + if (txt && *txt) { + txt_e = rr_create(dup_nlabel(nlabel), RR_TXT); // automatically 4500 seconds + rr_list_append(&service->entries, txt_e); + + // add TXTs + for (; *txt; txt++) + rr_add_txt(txt_e, *txt); + } + + // create SRV record + assert(hostname || svr->hostname); // either one as target + target = hostname ? create_nlabel(hostname) : dup_nlabel(svr->hostname); + + srv_e = rr_create_srv(dup_nlabel(nlabel), port, target); // automatically 4500 seconds + rr_list_append(&service->entries, srv_e); + + // create PTR record for type + ptr_e = rr_create_ptr(type_nlabel, srv_e); // automatically 4500 seconds + + // create services PTR record for type + // this enables the type to show up as a "service" + bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e); // automatically 4500 seconds + + // modify lists here + pthread_mutex_lock(&svr->data_lock); + + if (txt_e) + rr_group_add(&svr->group, txt_e); + rr_group_add(&svr->group, srv_e); + rr_group_add(&svr->group, ptr_e); + rr_group_add(&svr->group, bptr_e); + + // append PTR entry to announce list + rr_list_append(&svr->announce, ptr_e); + rr_list_append(&svr->services, ptr_e); + + pthread_mutex_unlock(&svr->data_lock); + + // don't free type_nlabel - it's with the PTR record + free(nlabel); + free(inst_nlabel); + + // notify server + write_pipe(svr->notify_pipe[1], ".", 1); + + return service; } void mdns_service_destroy(struct mdns_service *srv) { - assert(srv != NULL); - rr_list_destroy(srv->entries, 0); - free(srv); + assert(srv != NULL); + rr_list_destroy(srv->entries, 0); + free(srv); } struct mdnsd *mdnsd_start() { - pthread_t tid; - pthread_attr_t attr; - - struct mdnsd *server = malloc(sizeof(struct mdnsd)); - memset(server, 0, sizeof(struct mdnsd)); - - if (create_pipe(server->notify_pipe) != 0) { - log_message(LOG_ERR, "pipe(): %m\n"); - free(server); - return NULL; - } - - server->sockfd = create_recv_sock(); - if (server->sockfd < 0) { - log_message(LOG_ERR, "unable to create recv socket"); - free(server); - return NULL; - } - - pthread_mutex_init(&server->data_lock, NULL); - - // init thread - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - if (pthread_create(&tid, &attr, (void *(*)(void *)) main_loop, (void *) server) != 0) { - pthread_mutex_destroy(&server->data_lock); - free(server); - return NULL; - } - - return server; + pthread_t tid; + pthread_attr_t attr; + + struct mdnsd *server = malloc(sizeof(struct mdnsd)); + memset(server, 0, sizeof(struct mdnsd)); + + if (create_pipe(server->notify_pipe) != 0) { + log_message(LOG_ERR, "pipe(): %m\n"); + free(server); + return NULL; + } + + server->sockfd = create_recv_sock(); + if (server->sockfd < 0) { + log_message(LOG_ERR, "unable to create recv socket"); + free(server); + return NULL; + } + + pthread_mutex_init(&server->data_lock, NULL); + + // init thread + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if (pthread_create(&tid, &attr, (void *(*)(void *))main_loop, (void *)server) != 0) { + pthread_mutex_destroy(&server->data_lock); + free(server); + return NULL; + } + + return server; } void mdnsd_stop(struct mdnsd *s) { - assert(s != NULL); + assert(s != NULL); - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 500 * 1000, - }; + struct timeval tv = { + .tv_sec = 0, .tv_usec = 500 * 1000, + }; - s->stop_flag = 1; - write_pipe(s->notify_pipe[1], ".", 1); + s->stop_flag = 1; + write_pipe(s->notify_pipe[1], ".", 1); - while (s->stop_flag != 2) - select(0, NULL, NULL, NULL, &tv); + while (s->stop_flag != 2) + select(0, NULL, NULL, NULL, &tv); - close_pipe(s->notify_pipe[0]); - close_pipe(s->notify_pipe[1]); + close_pipe(s->notify_pipe[0]); + close_pipe(s->notify_pipe[1]); - pthread_mutex_destroy(&s->data_lock); - rr_group_destroy(s->group); - rr_list_destroy(s->announce, 0); - rr_list_destroy(s->services, 0); + pthread_mutex_destroy(&s->data_lock); + rr_group_destroy(s->group); + rr_list_destroy(s->announce, 0); + rr_list_destroy(s->services, 0); - if (s->hostname) - free(s->hostname); + if (s->hostname) + free(s->hostname); - free(s); + free(s); } diff --git a/tinysvcmdns.h b/tinysvcmdns.h index 98f26fa..c32c443 100644 --- a/tinysvcmdns.h +++ b/tinysvcmdns.h @@ -45,120 +45,118 @@ #include <arpa/inet.h> #endif -#define MALLOC_ZERO_STRUCT(x, type) \ - x = malloc(sizeof(struct type)); \ - memset(x, 0, sizeof(struct type)); - -#define DECL_MALLOC_ZERO_STRUCT(x, type) \ - struct type * MALLOC_ZERO_STRUCT(x, type) - +#define MALLOC_ZERO_STRUCT(x, type) \ + x = malloc(sizeof(struct type)); \ + memset(x, 0, sizeof(struct type)); +#define DECL_MALLOC_ZERO_STRUCT(x, type) struct type *MALLOC_ZERO_STRUCT(x, type) struct rr_data_srv { - uint16_t priority; - uint16_t weight; - uint16_t port; - uint8_t *target; // host + uint16_t priority; + uint16_t weight; + uint16_t port; + uint8_t *target; // host }; struct rr_data_txt { - struct rr_data_txt *next; - uint8_t *txt; + struct rr_data_txt *next; + uint8_t *txt; }; struct rr_data_nsec { - //uint8_t *name; // same as record + // uint8_t *name; // same as record - // NSEC occupies the 47th bit, 5 bytes - //uint8_t bitmap_len; // = 5 - uint8_t bitmap[5]; // network order: first byte contains LSB + // NSEC occupies the 47th bit, 5 bytes + // uint8_t bitmap_len; // = 5 + uint8_t bitmap[5]; // network order: first byte contains LSB }; struct rr_data_ptr { - uint8_t *name; // NULL if entry is to be used - struct rr_entry *entry; + uint8_t *name; // NULL if entry is to be used + struct rr_entry *entry; }; struct rr_data_a { - uint32_t addr; + uint32_t addr; }; struct rr_data_aaaa { - struct in6_addr *addr; + struct in6_addr *addr; }; struct rr_entry { - uint8_t *name; - - enum rr_type { - RR_A = 0x01, - RR_PTR = 0x0C, - RR_TXT = 0x10, - RR_AAAA = 0x1C, - RR_SRV = 0x21, - RR_NSEC = 0x2F, - RR_ANY = 0xFF, - } type; - - uint32_t ttl; - - // for use in Questions only - char unicast_query; - - // for use in Answers only - char cache_flush; - - uint16_t rr_class; - - // RR data - union { - struct rr_data_nsec NSEC; - struct rr_data_srv SRV; - struct rr_data_txt TXT; - struct rr_data_ptr PTR; - struct rr_data_a A; - struct rr_data_aaaa AAAA; - } data; + uint8_t *name; + + enum rr_type { + RR_A = 0x01, + RR_PTR = 0x0C, + RR_TXT = 0x10, + RR_AAAA = 0x1C, + RR_SRV = 0x21, + RR_NSEC = 0x2F, + RR_ANY = 0xFF, + } type; + + uint32_t ttl; + + // for use in Questions only + char unicast_query; + + // for use in Answers only + char cache_flush; + + uint16_t rr_class; + + // RR data + union { + struct rr_data_nsec NSEC; + struct rr_data_srv SRV; + struct rr_data_txt TXT; + struct rr_data_ptr PTR; + struct rr_data_a A; + struct rr_data_aaaa AAAA; + } data; }; struct rr_list { - struct rr_entry *e; - struct rr_list *next; + struct rr_entry *e; + struct rr_list *next; }; struct rr_group { - uint8_t *name; + uint8_t *name; - struct rr_list *rr; + struct rr_list *rr; - struct rr_group *next; + struct rr_group *next; }; -#define MDNS_FLAG_RESP (1 << 15) // Query=0 / Response=1 -#define MDNS_FLAG_AA (1 << 10) // Authoritative -#define MDNS_FLAG_TC (1 << 9) // TrunCation -#define MDNS_FLAG_RD (1 << 8) // Recursion Desired -#define MDNS_FLAG_RA (1 << 7) // Recursion Available -#define MDNS_FLAG_Z (1 << 6) // Reserved (zero) +#define MDNS_FLAG_RESP (1 << 15) // Query=0 / Response=1 +#define MDNS_FLAG_AA (1 << 10) // Authoritative +#define MDNS_FLAG_TC (1 << 9) // TrunCation +#define MDNS_FLAG_RD (1 << 8) // Recursion Desired +#define MDNS_FLAG_RA (1 << 7) // Recursion Available +#define MDNS_FLAG_Z (1 << 6) // Reserved (zero) -#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F) +#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F) #define MDNS_FLAG_GET_OPCODE(x) ((x >> 11) & 0x0F) // gets the PTR target name, either from "name" member or "entry" member -#define MDNS_RR_GET_PTR_NAME(rr) (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name) +#define MDNS_RR_GET_PTR_NAME(rr) \ + (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name) struct mdns_pkt { - uint16_t id; // transaction ID - uint16_t flags; - uint16_t num_qn; - uint16_t num_ans_rr; - uint16_t num_auth_rr; - uint16_t num_add_rr; - - struct rr_list *rr_qn; // questions - struct rr_list *rr_ans; // answer RRs - struct rr_list *rr_auth; // authority RRs - struct rr_list *rr_add; // additional RRs + uint16_t id; // transaction ID + uint16_t flags; + uint16_t num_qn; + uint16_t num_ans_rr; + uint16_t num_auth_rr; + uint16_t num_add_rr; + + struct rr_list *rr_qn; // questions + struct rr_list *rr_ans; // answer RRs + struct rr_list *rr_auth; // authority RRs + struct rr_list *rr_add; // additional RRs }; struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len); @@ -197,7 +195,7 @@ uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2); // compares 2 names static inline int cmp_nlabel(const uint8_t *L1, const uint8_t *L2) { - return strcmp((char *) L1, (char *) L2); + return strcmp((char *)L1, (char *)L2); } //******************************************************// @@ -225,10 +223,10 @@ void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr); // registers a service with the MDNS responder instance struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name, - const char *type, uint16_t port, const char *hostname, const char *txt[]); + const char *type, uint16_t port, const char *hostname, + const char *txt[]); // destroys the mdns_service struct returned by mdnsd_register_svc() void mdns_service_destroy(struct mdns_service *srv); - #endif // _TINYSVCMDNS_H |