Browse Source

Initial import

Georgi Chorbadzhiyski 3 years ago
commit
a9884634c6
19 changed files with 2717 additions and 0 deletions
  1. 3
    0
      .gitignore
  2. 3
    0
      .gitmodules
  3. 49
    0
      Makefile
  4. 379
    0
      commands.c
  5. 19
    0
      commands.h
  6. 239
    0
      conf.c
  7. 37
    0
      conf.h
  8. 20
    0
      config.channels
  9. 22
    0
      config.networks
  10. 423
    0
      data.c
  11. 160
    0
      data.h
  12. 74
    0
      defines.h
  13. 1
    0
      libfuncs
  14. 524
    0
      proxy_common.c
  15. 21
    0
      proxy_common.h
  16. 239
    0
      proxy_ts.c
  17. 14
    0
      request.h
  18. 235
    0
      request_tsiproxy.c
  19. 255
    0
      tsiproxy.c

+ 3
- 0
.gitignore View File

1
+*.o
2
+*.a
3
+tsiproxy

+ 3
- 0
.gitmodules View File

1
+[submodule "libfuncs"]
2
+	path = libfuncs
3
+	url = https://georgi.unixsol.org/git/gfto/libfuncs.git

+ 49
- 0
Makefile View File

1
+CC = $(CROSS)$(TARGET)gcc
2
+STRIP = $(CROSS)$(TARGET)strip
3
+INCLUDES = -include defines.h -I.
4
+BUILD_ID = $(shell git describe --tags)
5
+CFLAGS ?= $(INCLUDES) -DGIT_BUILD_ID=\"$(BUILD_ID)\" -D_GNU_SOURCE=1 \
6
+ -O2 -ggdb -pipe -ffunction-sections -fdata-sections \
7
+ -W -Wall -Wextra \
8
+ -Wshadow -Wformat-security \
9
+ -Wredundant-decls -Wold-style-definition
10
+
11
+RM = /bin/rm -f
12
+Q=@
13
+
14
+LIBS = -lpthread
15
+FUNCS_DIR = libfuncs
16
+FUNCS_LIB = $(FUNCS_DIR)/libfuncs.a
17
+
18
+tsiproxy_OBJS = data.o conf.o commands.o \
19
+	request_tsiproxy.o \
20
+	proxy_common.o proxy_ts.o \
21
+	tsiproxy.o \
22
+	$(FUNCS_LIB)
23
+
24
+tsiproxy: $(tsiproxy_OBJS)
25
+	$(Q)echo "  LINK	tsiproxy"
26
+	$(Q)$(CC) $(CFLAGS) $(tsiproxy_OBJS) $(LIBS) -o tsiproxy
27
+
28
+
29
+$(FUNCS_LIB):
30
+	$(Q)echo "  MAKE	$(FUNCS_LIB)"
31
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR)
32
+
33
+%.o: %.c defines.h
34
+	$(Q)echo "  CC	tsiproxy	$<"
35
+	$(Q)$(CC) -Dtsiproxy=1 $(CFLAGS)  -c $<
36
+
37
+strip:
38
+	$(Q)echo "  STRIP"
39
+	$(Q)$(STRIP) tsiproxy vod
40
+
41
+clean:
42
+	$(Q)echo "  RM	$(tsiproxy_OBJS)"
43
+	$(Q)$(RM) $(tsiproxy_OBJS) tsiproxy *~
44
+
45
+distclean: clean
46
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR) clean
47
+
48
+client:
49
+	rm -rf *.h *.c *.o *.vo *.eo Makefile libfuncs

+ 379
- 0
commands.c View File

1
+/* tsiproxy server commands */
2
+#include <stdlib.h>
3
+#include <stdio.h>
4
+#include <unistd.h>
5
+#include <string.h>
6
+#include <arpa/inet.h>
7
+#include <netinet/in.h>
8
+#include <sys/utsname.h>
9
+#include <sys/resource.h>
10
+#include <sys/types.h>
11
+#include <sys/stat.h>
12
+
13
+#include <errno.h>
14
+#include <dirent.h>
15
+#include <fcntl.h>
16
+
17
+#include "libfuncs/libfuncs.h"
18
+
19
+#include "data.h"
20
+#include "conf.h"
21
+
22
+extern CONFIG *config;
23
+extern char *server_ver;
24
+extern LIST *netconf, *clients, *chanconf, *restreamer;
25
+extern STATS allstats;
26
+
27
+static void std_headers(int clientsock, char *IP) {
28
+	fdputsf(clientsock, "X-IP: %s\n", IP);
29
+	send_header_textplain(clientsock);
30
+	fdputs(clientsock, "\n");
31
+}
32
+
33
+void run_ping(unsigned int clientsock, acl_t acl, uint32_t src_ip, char *IP) {
34
+	unused(src_ip);
35
+	char flags[6] = "-----";
36
+	send_200_ok(clientsock);
37
+	if (acl.stats ) flags[1] = 'S';
38
+	if (acl.reconf) flags[2] = 'F';
39
+	if (acl.access) flags[3] = 'A';
40
+	fdputsf(clientsock, "X-Access: %s\n", flags);
41
+	std_headers(clientsock, IP);
42
+	fdputs(clientsock, "PONG\n");
43
+}
44
+
45
+int __show_stats(unsigned int clientsock, acl_t acl, char *IP) {
46
+	if (!acl.stats) {
47
+		LOGf("DENY : Stats access denied | IP: %s\n", IP);
48
+		send_403_forbidden(clientsock);
49
+		return 0;
50
+	}
51
+	send_200_ok(clientsock);
52
+	std_headers(clientsock, IP);
53
+	return 1;
54
+}
55
+
56
+void show_stats(unsigned int clientsock, acl_t acl, char *IP) {
57
+	if (!__show_stats(clientsock, acl, IP))
58
+		return;
59
+	time_t now = time(NULL);
60
+	LNODE *l, *tmp;
61
+	list_lock(clients);
62
+	list_for_each_reverse(clients, l, tmp) {
63
+		CLIENT *c = l->data;
64
+		// Workaround for unknown bug that leaves records in clients list but not in restreamerX->clients list
65
+		// Such a client hangs forever taking memory. The bug was seen on snaps server
66
+		if (now - c->ts >= NO_TRAFFIC_SECONDS + 5) {
67
+			list_unlock(clients);
68
+			stop_client_shutdown(c);
69
+			list_lock(clients);
70
+			continue;
71
+		}
72
+		if (c->traffic_out == 0)
73
+			continue;
74
+		//                  IP   A  FD   PROV  CID         TRA TM  CHAN  AGENT
75
+		fdputsf(clientsock,"%s\t%c\t%i\t%-12s\t%lu\t%14llu\t%6lu\t%-22s\t%s\n",
76
+			c->IP,
77
+			'-',
78
+			c->fno,
79
+			"-",
80
+			c->clientid,
81
+			c->traffic_out,
82
+			now - c->start,
83
+			c->chan,
84
+			c->agent);
85
+	}
86
+	list_unlock(clients);
87
+}
88
+
89
+int __show_info(unsigned int clientsock, acl_t acl, char *IP,
90
+	unsigned long total_clients,
91
+	unsigned long clients_current, unsigned long clients_gone,
92
+	unsigned long long traffic_in, unsigned long long traffic_out)
93
+{
94
+	int used_fds = -1;
95
+	if (!acl.stats) {
96
+		LOGf("DENY : Info access denied | IP: %s\n", IP);
97
+		send_403_forbidden(clientsock);
98
+		return 0;
99
+	}
100
+	time_t now = time(NULL);
101
+	time_t uptime = now - allstats.start_ts;
102
+	unsigned int t_sec,t_min,t_hour,t_days;
103
+	struct rlimit limit;
104
+	double load_avg[3];
105
+
106
+	t_sec = uptime % 60;
107
+	t_min  = (uptime - t_sec) / 60;
108
+	t_hour = (t_min - t_min % 60) / 60;
109
+	t_min  = t_min - t_hour * 60;
110
+	t_days = (t_hour - t_hour % 24) / 24;
111
+	t_hour = t_hour - t_days * 24;
112
+
113
+	getrlimit(RLIMIT_NOFILE, &limit);
114
+	getloadavg(load_avg, 3);
115
+
116
+	send_200_ok(clientsock);
117
+	std_headers(clientsock, IP);
118
+	fdputs (clientsock,  "Server info\n");
119
+	fdputsf(clientsock, "  Ident            : %s\n", config->ident);
120
+	fdputsf(clientsock, "  Version          : %s\n", server_ver);
121
+	fdputsf(clientsock, "  Server           : %s %s %s %s %s\n",
122
+		allstats.utsdata.sysname,
123
+		allstats.utsdata.nodename,
124
+		allstats.utsdata.release,
125
+		allstats.utsdata.version,
126
+		allstats.utsdata.machine);
127
+	fdputsf(clientsock, "  Cores            : %ld\n", (long)sysconf(_SC_NPROCESSORS_ONLN));
128
+
129
+	DIR *d = opendir("/proc/self/fd");
130
+	if (d) {
131
+		struct dirent *dir;
132
+		while ((dir = readdir(d))) {
133
+			if (dir->d_name[0] == '.') // Ignore . and ..
134
+				continue;
135
+			used_fds++;
136
+		}
137
+		closedir(d);
138
+	}
139
+	fdputsf(clientsock, "  Max FDs          : %ld\n",(long)limit.rlim_max);
140
+	fdputsf(clientsock, "  Used FDs         : %ld\n",(long)used_fds);
141
+	fdputsf(clientsock, "  Load average     : %4.2f %4.2f %4.2f\n",load_avg[0],load_avg[1],load_avg[2]);
142
+	fdputsf(clientsock, "  Localtime        : %s", ctime(&now));
143
+	fdputsf(clientsock, "  Uptime           : %d %02d:%02d:%02d\n", t_days, t_hour, t_min, t_sec);
144
+	fdputsf(clientsock, "  Redirect         : %s %s\n\n",
145
+		config->redirect_url ? "enabled" : "disabled",
146
+		config->redirect_url ? config->redirect_url : ""
147
+	);
148
+
149
+	fdputs (clientsock, "Statistics\n");
150
+	fdputsf(clientsock, "  Clients current  : %lu\n",     clients_current);
151
+	fdputsf(clientsock, "  Clients cur+gone : %lu\n",     clients_current + clients_gone);
152
+	fdputsf(clientsock, "  Traffic in       : %llu MB\n", traffic_in / (1024*1024));
153
+	fdputsf(clientsock, "  Traffic out      : %llu MB\n", traffic_out / (1024*1024));
154
+	fdputsf(clientsock, "  Total clients    : %lu\n\n",   total_clients);
155
+
156
+	return 1;
157
+}
158
+
159
+void show_info(unsigned int clientsock, acl_t acl, char *IP) {
160
+	if (!__show_info(clientsock, acl, IP, allstats.clients, allstats.clients_current, allstats.clients_gone, allstats.traffic_in, allstats.traffic_out))
161
+		return;
162
+
163
+	time_t now = time(NULL);
164
+	time_t uptime = now - allstats.start_ts;
165
+	fdputs (clientsock, "IPTVD statistics\n");
166
+	fdputsf(clientsock, "  Proxies current  : %u\n",      restreamer->items);
167
+	fdputsf(clientsock, "  Restreamers      : %lu\n",     allstats.child_servers);
168
+	fdputsf(clientsock, "  Connects         : %lu\n",     allstats.clients_all);
169
+	fdputsf(clientsock, "  Errors           : %lu\n",     allstats.errors);
170
+	fdputs (clientsock, "                     current cur+gone trafic_in traffic_out clients servers connects errors localtime uptime\n");
171
+	fdputsf(clientsock, "AllStats           : %lu %lu %llu %llu %lu %lu %lu %lu %lu %lu\n\n",
172
+		allstats.clients_current,
173
+		allstats.clients_current + allstats.clients_gone,
174
+		allstats.traffic_in,
175
+		allstats.traffic_out,
176
+		allstats.clients,
177
+		allstats.child_servers,
178
+		allstats.clients_all,
179
+		allstats.errors,
180
+		now,
181
+		uptime);
182
+
183
+	LNODE *l, *tmp;
184
+	unsigned int t_sec,t_min,t_hour,t_days;
185
+	fdputs(clientsock, "Running restreamers\n");
186
+	list_lock(restreamer);
187
+	list_for_each_reverse(restreamer, l, tmp) {
188
+		RESTREAMER *r = l->data;
189
+		uptime = time(NULL) - r->started;
190
+
191
+		t_sec = uptime % 60;
192
+		t_min  = (uptime - t_sec) / 60;
193
+		t_hour = (t_min - t_min % 60) / 60;
194
+		t_min  = t_min - t_hour * 60;
195
+		t_days = (t_hour - t_hour % 24) / 24;
196
+		t_hour = t_hour - t_days * 24;
197
+
198
+		fdputsf(clientsock,"  %-27sClients: %i Served: %li Conn: %li Uptime: %d %02d:%02d:%02d In: %lli %lli MB Out: %lli %lli MB\n",
199
+			r->name,
200
+			r->clients->items,
201
+			r->served,
202
+			r->connects,
203
+			t_days, t_hour, t_min, t_sec,
204
+			r->traffic_in, r->traffic_in / (1024*1024),
205
+			r->traffic_out, r->traffic_out / (1024*1024)
206
+		);
207
+	}
208
+	list_unlock(restreamer);
209
+	fdputs(clientsock,"\n");
210
+}
211
+
212
+void run_livecheck(unsigned int clientsock, acl_t acl, char *IP) {
213
+	if (!acl.reconf) {
214
+		LOGf("DENY : LiveCheck access denied | IP: %s\n", IP);
215
+		send_403_forbidden(clientsock);
216
+		return;
217
+	}
218
+	send_200_ok(clientsock);
219
+	std_headers(clientsock, IP);
220
+	do {
221
+		char outbuf[128];
222
+		int size;
223
+		memset(outbuf, ' ', sizeof(outbuf));
224
+		outbuf[sizeof(outbuf)-1] = '\n';
225
+		STATS ls = allstats;
226
+		sleep(1);
227
+		time_t now = time(NULL);
228
+		time_t uptime = now - allstats.start_ts;
229
+		/* The traffic passed in the 1 second sleep above */
230
+		ls.traffic_in = allstats.traffic_in - ls.traffic_in;
231
+		ls.traffic_out = allstats.traffic_out - ls.traffic_out;
232
+		size = sprintf(outbuf, "%lu %lu %lu %u %llu %llu",
233
+				now,
234
+				uptime,
235
+				allstats.clients_current,
236
+				restreamer ? restreamer->items : 0,
237
+				ls.traffic_in,
238
+				ls.traffic_out);
239
+		outbuf[size]=' ';
240
+		if (fdwrite(clientsock, outbuf, sizeof(outbuf)) <= 0)
241
+			break;
242
+	} while (1);
243
+}
244
+
245
+void read_info(unsigned int clientsock, acl_t acl, char *IP) {
246
+	if (!acl.stats) {
247
+		LOGf("DENY : Info access denied | IP: %s\n", IP);
248
+		send_403_forbidden(clientsock);
249
+		return;
250
+	}
251
+	time_t now = time(NULL);
252
+	time_t uptime = now - allstats.start_ts;
253
+	send_200_ok(clientsock);
254
+	std_headers(clientsock, IP);
255
+	unsigned int gone = allstats.clients_gone;	/* Reset it first because fdputsf can block */
256
+	allstats.clients_gone = 0;
257
+	fdputsf(clientsock, "%lu %lu %llu %llu %lu %lu %lu %lu %lu %lu\n",
258
+		allstats.clients_current,
259
+		allstats.clients_current + gone,
260
+		allstats.traffic_in,
261
+		allstats.traffic_out,
262
+		allstats.clients,
263
+		allstats.child_servers,
264
+		allstats.clients_all,
265
+		allstats.errors,
266
+		now,
267
+		uptime);
268
+}
269
+
270
+void run_netconf(unsigned int clientsock, acl_t acl, char *IP) {
271
+	if (!acl.reconf) {
272
+		LOGf("DENY : NetConf access denied | IP: %s\n", IP);
273
+		send_403_forbidden(clientsock);
274
+		return;
275
+	}
276
+	int nets = load_networks(config);
277
+	if (nets > -1)
278
+		LOGf("CONF : NetConf reloaded | IP: %s Loaded: %i\n", IP, nets);
279
+	send_200_ok(clientsock);
280
+	std_headers(clientsock, IP);
281
+	if (nets > -1)
282
+		fdputsf(clientsock,"OK %i networks read\n", nets);
283
+	else
284
+		fdputs(clientsock,"Networks are already up to date\n");
285
+}
286
+
287
+void run_chanconf(unsigned int clientsock, acl_t acl, char *IP) {
288
+	if (!acl.reconf) {
289
+		LOGf("DENY : ChanConf access denied | IP: %s\n", IP);
290
+		send_403_forbidden(clientsock);
291
+		return;
292
+	}
293
+	int j = load_channels(config);
294
+	if (j > -1)
295
+		LOGf("CONF : ChanConf reloaded | IP: %s Loaded: %i\n", IP, j);
296
+	send_200_ok(clientsock);
297
+	std_headers(clientsock, IP);
298
+	if (j > -1)
299
+		fdputsf(clientsock,"OK %i channels read\n",j);
300
+	else
301
+		fdputs(clientsock,"Channels are already up to date\n");
302
+}
303
+
304
+void run_getnetconf(unsigned int clientsock, acl_t acl, char *IP) {
305
+	if (!acl.reconf) {
306
+		send_403_forbidden(clientsock);
307
+		return;
308
+	}
309
+	send_200_ok(clientsock);
310
+	std_headers(clientsock, IP);
311
+	LNODE *l, *tmp;
312
+	list_lock(netconf);
313
+	list_for_each(netconf, l, tmp) {
314
+		NETWORK *n = l->data;
315
+		fdputsf(clientsock,"%d.%d.%d.%d/%d.%d.%d.%d\t%s%s%s\n",
316
+			(n->net >> 0 ) & 0xFF,
317
+			(n->net >> 8 ) & 0xFF,
318
+			(n->net >> 16) & 0xFF,
319
+			(n->net >> 24) & 0xFF,
320
+
321
+			(n->mask >> 0 ) & 0xFF,
322
+			(n->mask >> 8 ) & 0xFF,
323
+			(n->mask >> 16) & 0xFF,
324
+			(n->mask >> 24) & 0xFF,
325
+
326
+			n->acl.stats  ? "S" : "",
327
+			n->acl.reconf ? "F" : "",
328
+			n->acl.access ? "A" : ""
329
+		);
330
+	}
331
+	list_unlock(netconf);
332
+}
333
+
334
+void run_getchanconf(unsigned int clientsock, acl_t acl, char *IP) {
335
+	if (!acl.reconf) {
336
+		send_403_forbidden(clientsock);
337
+		return;
338
+	}
339
+	send_200_ok(clientsock);
340
+	std_headers(clientsock, IP);
341
+	LNODE *l, *tmp;
342
+	list_lock(chanconf);
343
+	list_for_each(chanconf, l, tmp) {
344
+		CHANNEL *c = l->data;
345
+		int r = 0;
346
+		while (c->sources[r] && r < MAX_CHANNEL_SOURCES) {
347
+			fdputsf(clientsock,"%s\t%s\n", c->name, c->sources[r]);
348
+			r++;
349
+		}
350
+	}
351
+	list_unlock(chanconf);
352
+}
353
+
354
+void run_channels(unsigned int clientsock, acl_t acl, char *IP) {
355
+	time_t now = time(NULL);
356
+	LNODE *l, *tmp;
357
+	if (!acl.stats) {
358
+		LOGf("DENY : Channels access denied | IP: %s\n", IP);
359
+		send_403_forbidden(clientsock);
360
+		return;
361
+	}
362
+	send_200_ok(clientsock);
363
+	std_headers(clientsock, IP);
364
+	list_lock(restreamer);
365
+	list_for_each_reverse(restreamer, l, tmp) {
366
+		RESTREAMER *r = l->data;
367
+		time_t uptime = now - r->started;
368
+		fdputsf(clientsock,"%-20s\t%5i\t%5li\t%5li\t%7ld\t%12lli\t%12lli\n",
369
+			r->name,
370
+			r->clients->items,
371
+			r->served,
372
+			r->connects,
373
+			uptime,
374
+			r->traffic_in,
375
+			r->traffic_out
376
+		);
377
+	}
378
+	list_unlock(restreamer);
379
+}

+ 19
- 0
commands.h View File

1
+/* tsiproxy server commands */
2
+#ifndef COMMANDS_H
3
+# define COMMANDS_H
4
+
5
+#include <arpa/inet.h>
6
+#include <netinet/in.h>
7
+
8
+void run_ping(unsigned int clientsock, acl_t acl, uint32_t src_ip, char *IP);
9
+void show_stats(unsigned int clientsock, acl_t acl, char *IP);
10
+void show_info(unsigned int clientsock, acl_t acl, char *IP);
11
+void read_info(unsigned int clientsock, acl_t acl, char *IP);
12
+void run_netconf(unsigned int clientsock, acl_t acl, char *IP);
13
+void run_chanconf(unsigned int clientsock, acl_t acl, char *IP);
14
+void run_getnetconf(unsigned int clientsock, acl_t acl, char *IP);
15
+void run_getchanconf(unsigned int clientsock, acl_t acl, char *IP);
16
+void run_livecheck(unsigned int clientsock, acl_t acl, char *IP);
17
+void run_channels(unsigned int clientsock, acl_t acl, char *IP);
18
+
19
+#endif

+ 239
- 0
conf.c View File

1
+/* tsiproxy server configuration */
2
+#include <stdlib.h>
3
+#include <stdio.h>
4
+#include <string.h>
5
+#include <unistd.h>
6
+#include <sys/time.h>
7
+
8
+#include <fcntl.h>
9
+#include <errno.h>
10
+#include <regex.h>
11
+#include <pthread.h>
12
+
13
+#include "libfuncs/libfuncs.h"
14
+
15
+#include "data.h"
16
+#include "conf.h"
17
+
18
+extern LIST *netconf, *chanconf, *restreamer;
19
+
20
+static pthread_mutex_t networks_lock  = PTHREAD_MUTEX_INITIALIZER;
21
+static pthread_mutex_t channels_lock  = PTHREAD_MUTEX_INITIALIZER;
22
+
23
+int load_channels(CONFIG *config) {
24
+	regex_t re;
25
+	regmatch_t res[3];
26
+	char line[1024];
27
+	int fd;
28
+	int num_channels = 0;
29
+
30
+	if (pthread_mutex_trylock(&channels_lock) != 0)
31
+		return -1;
32
+
33
+	fd = open(config->channels_file, O_RDONLY);
34
+
35
+	if (fd != -1) {
36
+		struct timeval tv;
37
+		gettimeofday(&tv, NULL);
38
+		unsigned int randstate = tv.tv_usec;
39
+		int cookie = rand_r(&randstate);
40
+
41
+		regcomp(&re, "^([A-Za-z0-9/\\.]+)\t+([A-Za-z0-9/\\.:-]+)", REG_EXTENDED);
42
+		time_t curtime = time(NULL);
43
+		LIST *old_chanconf;
44
+		LIST *new_chanconf = list_new("chanconf");
45
+		while (fdgetline(fd,line,sizeof(line)) > 0) {
46
+			chomp(line);
47
+			if (regexec(&re,line,3,res,0)==0) {
48
+				char *name, *source;
49
+				char *org = strdup(line);
50
+				name   = org+res[1].rm_so; org[res[1].rm_eo]=0;
51
+				source = org+res[2].rm_so; org[res[2].rm_eo]=0;
52
+				if (!is_valid_url(source)) {
53
+					LOGf("CCONF: Invalid url: %s\n", source);
54
+					FREE(org);
55
+					goto report_error;
56
+				}
57
+				/* Search for already added channel */
58
+				LNODE *l, *tmp;
59
+				CHANNEL *chan = NULL;
60
+				list_for_each_reverse(new_chanconf, l, tmp) {
61
+					if (strcmp(name, ((CHANNEL *)l->data)->name)==0) {
62
+						chan = l->data;
63
+						break;
64
+					}
65
+				}
66
+				if (!chan) {
67
+					list_add(new_chanconf, new_channel(name, source));
68
+					num_channels++;
69
+				} else {
70
+					add_channel_source(chan, source);
71
+				}
72
+				FREE(org);
73
+			} else {
74
+report_error:
75
+				if (strlen(line) > 2 && line[0] != '#') {
76
+					LOGf("CCONF: Invalid config line: %s\n", line);
77
+				}
78
+			}
79
+			if (time(NULL) - curtime > CONFIG_LOAD_TIMEOUT) {
80
+				LOGf("CCONF: Read timeout after %d lines on %s\n", num_channels, line);
81
+				break;
82
+			}
83
+		}
84
+		regfree(&re);
85
+		shutdown_fd(&fd);
86
+		/* Save current chanconf */
87
+		old_chanconf = chanconf;
88
+		/* Switch chanconf */
89
+		chanconf = new_chanconf;
90
+		/* Rewrite restreamer channels */
91
+		LNODE *lc, *lr, *lctmp, *lrtmp;
92
+		CHANNEL *chan;
93
+		list_lock(restreamer);	// Unlocked after second list_for_each(restreamer)
94
+
95
+		list_lock(chanconf);
96
+		list_for_each(chanconf, lc, lctmp) {
97
+			chan = lc->data;
98
+			list_for_each(restreamer, lr, lrtmp) {
99
+				if (strcmp(chan->name, ((RESTREAMER *)lr->data)->name)==0) {
100
+					RESTREAMER *restr = lr->data;
101
+					/* Mark the restreamer as valid */
102
+					restr->cookie = cookie;
103
+					/* Check if current source exists in new channel configuration */
104
+					int i, src_found = -1;
105
+					char *old_source = restr->channel->source;
106
+					for (i=0; i<chan->num_src; i++) {
107
+						if (strcmp(old_source, chan->sources[i]) == 0) {
108
+							src_found = i;
109
+						}
110
+					}
111
+					if (src_found > -1) {
112
+						/* New configuration contains existing source, just update the reference */
113
+						set_channel_source(chan, src_found);
114
+						restr->channel = chan;
115
+					} else {
116
+						/* New configuration *DO NOT* contain existing source. Force reconnect */
117
+						LOGf("RESTR: Source changed | Channel: %s srv_fd: %d Old:%s New:%s\n", chan->name, restr->sock, restr->channel->source, chan->source);
118
+						/* The order is important! */
119
+						set_channel_source(chan, chan->num_src-1); /* Set source to last one. On reconnect next source will be used. */
120
+						restr->channel = chan;
121
+						restr->reconnect = 1;
122
+					}
123
+					break;
124
+				}
125
+			}
126
+		}
127
+		list_unlock(chanconf);
128
+
129
+		/* Kill restreamers that serve channels that no longer exist */
130
+		list_for_each(restreamer, lr, lrtmp) {
131
+			RESTREAMER *r = lr->data;
132
+			/* This restreamer should no longer serve clients */
133
+			if (r->cookie != cookie) {
134
+				LOGf("RESTR: Channel removed | Channel: %s Source: %s\n", r->channel->name, r->channel->source);
135
+				/* Replace channel reference with real object and instruct free_restreamer to free it */
136
+				r->channel = new_channel(r->channel->name, r->channel->source);
137
+				r->freechannel = 1;
138
+				r->dienow = 1;
139
+			}
140
+		}
141
+		list_unlock(restreamer);
142
+
143
+		/* Free old_chanconf */
144
+		list_free(&old_chanconf, (void (*)(void *))free_channel, NULL);
145
+	} else {
146
+		num_channels = -1;
147
+	}
148
+	pthread_mutex_unlock(&channels_lock);
149
+	return num_channels;
150
+}
151
+
152
+int load_networks(CONFIG *config) {
153
+	char line[1024];
154
+	regex_t re;
155
+	regmatch_t res[8];
156
+	int fd;
157
+	int num_networks = 0;
158
+
159
+	if (pthread_mutex_trylock(&networks_lock) != 0)
160
+		return -1;
161
+
162
+	fd = open(config->networks_file, O_RDONLY);
163
+
164
+	if (fd != -1) {
165
+		regcomp(&re,"^([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})/([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})\t*([CFNSA]*)", REG_EXTENDED);
166
+		time_t curtime = time(NULL);
167
+		LIST *new_netconf = list_new("netconf");
168
+		while (fdgetline(fd,line,sizeof(line))>0){
169
+			if (regexec(&re,line,4,res,0)==0){
170
+				chomp(line);
171
+				char *_net, *_mask, *_flags;
172
+				_net  = line+res[1].rm_so; line[res[1].rm_eo]=0;
173
+				_mask = line+res[2].rm_so; line[res[2].rm_eo]=0;
174
+				_flags= line+res[3].rm_so; line[res[3].rm_eo]=0;
175
+
176
+				uint32_t net, mask;
177
+				inet_pton(AF_INET, _net, &net);
178
+				inet_pton(AF_INET,_mask, &mask);
179
+				net = net & mask;
180
+
181
+				acl_t acl;
182
+				acl.reconf = strchr(_flags,'F') != NULL;
183
+				acl.stats  = strchr(_flags,'S') != NULL;
184
+				acl.access = strchr(_flags,'A') != NULL; // bg network?
185
+
186
+				list_add(new_netconf, new_network(net, mask, acl));
187
+				num_networks++;
188
+			} else {
189
+				if (strlen(line) > 2 && line[0] != '#') {
190
+					LOGf("NCONF: Invalid config line: %s\n", line);
191
+				}
192
+			}
193
+			if (time(NULL) - curtime > CONFIG_LOAD_TIMEOUT) {
194
+				LOGf("NCONF: Read timeout after %d lines on %s\n", num_networks, line);
195
+				break;
196
+			}
197
+		}
198
+		regfree(&re);
199
+		shutdown_fd(&fd);
200
+		/* Save current netconf */
201
+		LIST *old_netconf = netconf;
202
+		/* Switch chanconf */
203
+		netconf = new_netconf;
204
+		/* Free old_netconf */
205
+		list_free(&old_netconf, (void (*)(void *))free_network, NULL);
206
+	} else {
207
+		num_networks = -1;
208
+	}
209
+	pthread_mutex_unlock(&networks_lock);
210
+	return num_networks;
211
+}
212
+
213
+CONFIG *config_alloc(void) {
214
+	CONFIG *c = calloc(1, sizeof(CONFIG));
215
+	c->server_port    = 8080;
216
+	return c;
217
+}
218
+
219
+int config_redirect_enabled(CONFIG *conf, int clientsock, char *path) {
220
+	char *url;
221
+	if (!conf->redirect_url)
222
+		return 0;
223
+	asprintf(&url, "%s%s", conf->redirect_url, path);
224
+	send_302_redirect(clientsock, url);
225
+	free(url);
226
+	return 1;
227
+}
228
+
229
+void config_free(CONFIG **pconf) {
230
+	CONFIG *conf = *pconf;
231
+	if (conf) {
232
+		if (conf->pidfile)
233
+			unlink(conf->pidfile);
234
+		FREE(conf->host_ptr);
235
+		FREE(conf->logident);
236
+		FREE(conf->redirect_url);
237
+		FREE(*pconf);
238
+	}
239
+}

+ 37
- 0
conf.h View File

1
+/* tsiproxy server configuration */
2
+#ifndef CONF_H
3
+# define CONF_H
4
+
5
+#include "data.h"
6
+
7
+typedef struct {
8
+	unsigned int			server_port; /* The port that iptvd will bind to */
9
+	int						server_socket;
10
+	struct sockaddr_in		server_name;
11
+	struct hostent			*host_ptr;
12
+
13
+	char					*pidfile;
14
+
15
+	char					*ident;
16
+	char					*logident;
17
+
18
+	char					*bind_to;			/* The address that ipdv will bind to */
19
+
20
+	char					*channels_file;		/* File in channels format (used for debugging) */
21
+	char					*networks_file;		/* File in networks format (used for debugging) */
22
+
23
+	char					*redirect_url;		/* Redirect all requests to this server */
24
+
25
+	int						disable_burst;		/* burst mpeg buffer to clients */
26
+	int						ts_sync;			/* ts proxy syncronises first 7 packets */
27
+} CONFIG;
28
+
29
+int load_channels(CONFIG *config);
30
+int load_networks(CONFIG *config);
31
+
32
+CONFIG *	config_alloc	();
33
+void		config_free		(CONFIG **conf);
34
+
35
+int			config_redirect_enabled(CONFIG *conf, int clientsock, char *path);
36
+
37
+#endif

+ 20
- 0
config.channels View File

1
+# Format of this file
2
+#
3
+#access_path		source_address
4
+#
5
+# Access path is where that stream is accessible via:
6
+#
7
+#  http://$TSIPROXY_HOST:$TSIPROXY_PORT/$access_path
8
+#
9
+# Source_address can be:
10
+#
11
+#  UDP input via unicast or multicast UDP:
12
+#    Example: udp://237.151.0.1:5000/
13
+#
14
+#  HTTP input via URL:
15
+#    Example: http://10.0.1.151:9090/channels/1.mpg
16
+#    Example: http://source.example.com:8080/something/2.mpg
17
+
18
+channels/1.mpg		udp://237.151.0.1:5000/
19
+channels/2.mpg		http://10.0.1.151:9090/channels/test.mpg
20
+channels/3.mpg		http://isp4.iptv.bg:8080/channels/input.mpg

+ 22
- 0
config.networks View File

1
+# Format of this file:
2
+#network/netmask		FLAGS
3
+#
4
+# Flags:
5
+#   A - access
6
+#   F - allow reconf
7
+#   S - allow access to stats
8
+#
9
+# Setting F or S assumes that A is also se
10
+#
11
+
12
+# The following address are allowed to access server config commands and stats
13
+10.0.1.0/255.255.255.0			AFS
14
+10.0.2.128/255.255.255.128		AFS
15
+127.0.0.0/255.0.0.0				AFS
16
+
17
+# The following addresses can "watch" the server streams
18
+12.34.56.78/255.255.255.255		A
19
+
20
+# The whole IPv4 internet is allowed
21
+0.0.0.0/128.0.0.0				A
22
+128.0.0.0/128.0.0.0				A

+ 423
- 0
data.c View File

1
+/* tsiproxy data functions */
2
+#include <stdio.h>
3
+#include <unistd.h>
4
+#include <stdlib.h>
5
+#include <string.h>
6
+#include <arpa/inet.h>
7
+#include <netinet/in.h>
8
+#include <sys/utsname.h>
9
+#include <sys/resource.h>
10
+
11
+#include <fcntl.h>
12
+#include <errno.h>
13
+#include <regex.h>
14
+
15
+#include "libfuncs/libfuncs.h"
16
+
17
+#include "conf.h"
18
+#include "data.h"
19
+
20
+extern int clean_on_exit;
21
+extern LIST *clients;
22
+extern STATS allstats;
23
+extern CONFIG *config;
24
+
25
+channel_source get_sproto(char *url) {
26
+	return strncmp(url, "http", 4)==0 ? tcp_sock : udp_sock;
27
+}
28
+
29
+CHANSRC *init_chansrc(char *url) {
30
+	regex_t re;
31
+	regmatch_t res[5];
32
+	regcomp(&re, "^([a-z]+)://([^:/?]+):?([0-9]*)/?(.*)", REG_EXTENDED);
33
+	if (regexec(&re,url,5,res,0)==0) {
34
+		char *data = strdup(url);
35
+		char *proto, *host, *port, *path;
36
+		int iport;
37
+		proto= data+res[1].rm_so; data[res[1].rm_eo]=0;
38
+		host = data+res[2].rm_so; data[res[2].rm_eo]=0;
39
+		port = data+res[3].rm_so; data[res[3].rm_eo]=0;
40
+		path = data+res[4].rm_so; data[res[4].rm_eo]=0;
41
+		iport = atoi(port);
42
+		/* Setup */
43
+		CHANSRC *src = calloc(1, sizeof(CHANSRC));
44
+		src->proto = strdup(proto);
45
+		src->sproto= get_sproto(url);
46
+		src->host  = strdup(host);
47
+		src->port  = iport ? iport : 80;
48
+		src->path  = strdup(path);
49
+		FREE(data);
50
+		regfree(&re);
51
+		return src;
52
+	}
53
+	regfree(&re);
54
+	return NULL;
55
+}
56
+
57
+void free_chansrc(CHANSRC *url) {
58
+	if (url) {
59
+		FREE(url->proto);
60
+		FREE(url->host);
61
+		FREE(url->path);
62
+		FREE(url);
63
+	}
64
+};
65
+
66
+int is_valid_url(char *url) {
67
+	regex_t re;
68
+	regmatch_t res[5];
69
+	int ret;
70
+	regcomp(&re, "^([a-z]+)://([^:/?]+):?([0-9]*)/?(.*)", REG_EXTENDED);
71
+	ret = regexec(&re,url,5,res,0);
72
+	regfree(&re);
73
+	return ret == 0;
74
+}
75
+
76
+void add_channel_source(CHANNEL *c, char *src) {
77
+	if (c->num_src >= MAX_CHANNEL_SOURCES-1)
78
+		return;
79
+	c->sources[c->num_src] = strdup(src);
80
+	if (c->num_src == 0) /* Set default source to first one */
81
+		c->source = c->sources[c->num_src];
82
+	c->num_src++;
83
+}
84
+
85
+void next_channel_source(CHANNEL *c) {
86
+	if (c->num_src <= 1)
87
+		return;
88
+	// uint8_t old_src = c->curr_src;
89
+	c->curr_src++;
90
+	if (c->curr_src >= MAX_CHANNEL_SOURCES-1 || c->sources[c->curr_src] == NULL)
91
+		c->curr_src = 0;
92
+	c->source = c->sources[c->curr_src];
93
+	// LOGf("CHAN : Switch source | Channel: %s OldSrc: %d %s NewSrc: %d %s\n", c->name, old_src, c->sources[old_src], c->curr_src, c->source);
94
+}
95
+
96
+void set_channel_source(CHANNEL *c, uint8_t src_id) {
97
+	if (src_id >= MAX_CHANNEL_SOURCES-1 || c->sources[src_id] == NULL)
98
+		return;
99
+	// uint8_t old_src = c->curr_src;
100
+	c->curr_src = src_id;
101
+	c->source = c->sources[c->curr_src];
102
+	// LOGf("CHAN : Set source    | Channel: %s OldSrc: %d %s NewSrc: %d %s\n", c->name, old_src, c->sources[old_src], c->curr_src, c->source);
103
+}
104
+
105
+CHANNEL * new_channel(char *name, char *source) {
106
+	CHANNEL *c = calloc(1, sizeof(CHANNEL));
107
+	c->name = strdup(name);
108
+	add_channel_source(c, source);
109
+	return c;
110
+}
111
+
112
+void free_channel(CHANNEL *c) {
113
+	int i;
114
+	for (i=c->num_src-1; i>=0; i--) {
115
+		FREE(c->sources[i]);
116
+	}
117
+	FREE(c->name);
118
+	c->source = NULL;
119
+	FREE(c);
120
+}
121
+
122
+int channel_search(LIST *channels_list, char *name, CHANNEL **found_channel) {
123
+	int found = 0;
124
+	LNODE *l, *tmp;
125
+	list_for_each(channels_list, l, tmp) {
126
+		CHANNEL *c = l->data;
127
+		if (strcmp(c->name, name) == 0) {
128
+			*found_channel = c;
129
+			found = 1;
130
+			break;
131
+		}
132
+	}
133
+	return found;
134
+}
135
+
136
+
137
+NETWORK * new_network(uint32_t net, uint32_t mask, acl_t acl) {
138
+	NETWORK *n = calloc(1, sizeof(NETWORK));
139
+	n->net  = net;
140
+	n->mask = mask;
141
+	n->acl  = acl;
142
+	return n;
143
+}
144
+
145
+void free_network(NETWORK *n) {
146
+	FREE(n);
147
+}
148
+
149
+int acl_search(LIST *acl_list, uint32_t search_ip, acl_t *found_acl) {
150
+	int found = 0;
151
+	LNODE *l, *tmp;
152
+	list_lock(acl_list);
153
+	list_for_each(acl_list, l, tmp) {
154
+		NETWORK *n = l->data;
155
+		if ((search_ip & n->mask) == n->net) {
156
+			*found_acl = n->acl;
157
+			found = 1;
158
+			break;
159
+		}
160
+	}
161
+	list_unlock(acl_list);
162
+	return found;
163
+}
164
+
165
+RESTREAMER * new_restreamer(const char *name, CHANNEL *channel) {
166
+	RESTREAMER *r = calloc(1, sizeof(RESTREAMER));
167
+	r->started = time(NULL);
168
+	r->name = strdup(name);
169
+	r->sock = -1;
170
+	r->queue = queue_new();
171
+	r->clients = list_new("r->clients");
172
+	r->channel = channel;
173
+	return r;
174
+}
175
+
176
+void free_restreamer(RESTREAMER *r) {
177
+	if (r->sock > -1)
178
+		shutdown_fd(&(r->sock));
179
+	if (r->freechannel)
180
+		free_channel(r->channel);
181
+	queue_free(&r->queue);
182
+	list_free(&r->clients, (void (*)(void *))stop_client_shutdown, NULL);
183
+	FREE(r->name);
184
+	FREE(r);
185
+}
186
+
187
+int restreamer_stop(LIST *proxys, char *channel) {
188
+	int matched = 0;
189
+	LNODE *l, *tmp;
190
+	list_lock(proxys);
191
+	list_for_each(proxys, l, tmp) {
192
+		RESTREAMER *r = l->data;
193
+		int doit = !channel || strcmp(channel, r->channel->name) == 0;
194
+		if (doit) {
195
+			matched++;
196
+			r->dienow = 1;
197
+		}
198
+	}
199
+	list_unlock(proxys);
200
+	return matched;
201
+}
202
+
203
+int restreamer_reconnect(LIST *proxys, char *channel) {
204
+	int matched = 0;
205
+	LNODE *l, *tmp;
206
+	list_lock(proxys);
207
+	list_for_each(proxys, l, tmp) {
208
+		RESTREAMER *r = l->data;
209
+		int doit = !channel || strcmp(channel, r->channel->name) == 0;
210
+		if (doit) {
211
+			matched++;
212
+			r->reconnect = 1;
213
+		}
214
+	}
215
+	list_unlock(proxys);
216
+	return matched;
217
+}
218
+
219
+void restreamer_stop_all(LIST *proxys) {
220
+	if (restreamer_stop(proxys, NULL)) {
221
+		int max_loops = 30; // 3 seconds
222
+		while (proxys->items > 0 && max_loops-- > 0)
223
+			usleep(100000);
224
+		if (proxys->items)
225
+			clean_on_exit = 0;
226
+	}
227
+}
228
+
229
+
230
+/* CLIENT FUNCTIONS */
231
+
232
+int is_child_server(char *user_agent, unsigned int client_id) {
233
+	if (!user_agent)
234
+		return 0;
235
+	return client_id == 0 && strcasestr(user_agent,"iptvd") == user_agent;
236
+}
237
+
238
+int is_ext(char *path, char *ext) {
239
+	return strncmp(ext, (path+strlen(path)-strlen(ext)), strlen(ext)) == 0;
240
+}
241
+
242
+CLIENT * new_client(int fd, time_t expire, char *chan, char *IP, char *agent, unsigned long clientid, acl_t acl, uint16_t client_port, int smart_client) {
243
+	CLIENT *nc = calloc(sizeof(CLIENT),1);
244
+
245
+	nc->fno      = fd;
246
+	nc->start    = time(NULL);
247
+	nc->ts       = nc->start;
248
+	nc->expire   = expire;
249
+	nc->chan	 = strdup(chan);
250
+	nc->IP       = strdup(IP);
251
+	inet_pton(AF_INET, IP, &(nc->ip));
252
+	nc->acl      = acl;
253
+	nc->clientid = clientid;
254
+	nc->client_port = client_port;
255
+	if (agent) {
256
+		nc->agent = strdup(agent);
257
+		nc->is_child_server = is_child_server(agent, clientid);
258
+	}
259
+	if (nc->is_child_server || smart_client)
260
+		nc->smart_client = 1;
261
+	list_add(clients, nc);
262
+	set_sock_nonblock(fd);
263
+	allstats.clients_all++;
264
+	return nc;
265
+}
266
+
267
+static void __client_log_connect(CLIENT *c) {
268
+	c->logged = 1;
269
+	if (c->client_port == 0) {
270
+		LOGf("NEW  : fd: %i Valid: %li | Client: %lu IP: %s Channel: %s Agent: %s\n",
271
+		      c->fno,
272
+		     (c->expire < 0 ? c->expire : -(c->start - c->expire)),
273
+		      c->clientid, c->IP, c->chan, c->agent);
274
+	} else {
275
+		LOGf("NEW  : fd: %i Valid: %li Port: %u | Client: %lu IP: %s Channel: %s Agent: %s\n",
276
+		      c->fno,
277
+		     (c->expire < 0 ? c->expire : -(c->start - c->expire)),
278
+		      c->client_port,
279
+		      c->clientid, c->IP, c->chan, c->agent);
280
+	}
281
+	allstats.clients_current++;
282
+	if (c->is_child_server)
283
+		allstats.child_servers++;
284
+	else
285
+		allstats.clients++;
286
+}
287
+
288
+void client_log_connect(CLIENT *c) {
289
+	if (c->logged)
290
+		return;
291
+	__client_log_connect(c);
292
+}
293
+
294
+void client_log_disconnect(CLIENT *c, char mark) {
295
+	if (!c->logged)
296
+		return;
297
+	allstats.clients_current--;
298
+	allstats.clients_gone++;
299
+	LOGf("STOP%c: fd: %i Bytes: %llu Seconds: %li | Client: %lu IP: %s Channel: %s Agent: %s\n",
300
+	     mark, c->fno, c->traffic_out, time(NULL)-c->start,
301
+	     c->clientid, c->IP, c->chan, c->agent);
302
+	time_t now = time(NULL);
303
+	if (now - c->start > NO_LOG_END_SECONDS) {
304
+		if (c->traffic_out > 0) {
305
+			char date[256];
306
+			struct tm tmres;
307
+			strftime(date,sizeof(date),"%d/%b/%Y:%H:%M:%S %z",localtime_r(&c->start, &tmres));
308
+			LOGf("LOG{%s %li - [%s] \"%s %lu %s\" 200 %llu \"%lu\" \"%s\"}\n",c->IP,now-c->start,date,
309
+				"-", c->clientid, c->chan, c->traffic_out, c->start, c->agent);
310
+		}
311
+	}
312
+}
313
+
314
+void free_client(CLIENT *c) {
315
+	FREE(c->chan);
316
+	FREE(c->IP);
317
+	FREE(c->agent);
318
+	FREE(c);
319
+}
320
+
321
+void stop_client(CLIENT *c, int socket_shutdown, char mark) {
322
+	if (!c || c->stopping)
323
+		return;
324
+	c->stopping = 1;
325
+	int client_socket = c->fno;
326
+	client_log_disconnect(c, mark);
327
+	list_del_entry(clients, c);
328
+	free_client(c);
329
+	if (socket_shutdown)
330
+		shutdown_fd(&client_socket);
331
+}
332
+
333
+void stop_client_noshutdown(CLIENT *c) {
334
+	stop_client(c, 0, ' ');
335
+}
336
+
337
+void stop_client_shutdown(CLIENT *c) {
338
+	stop_client(c, 1, ' ');
339
+}
340
+
341
+void stop_client_shutdown_mark(CLIENT *c, char mark) {
342
+	stop_client(c, 1, mark);
343
+}
344
+
345
+int control_client_stop(long clientid) {
346
+	int found = 0;
347
+	LNODE *l, *tmp;
348
+	list_lock(clients);
349
+	list_for_each(clients, l, tmp) {
350
+		CLIENT *c = l->data;
351
+		if (clientid == -1 || clientid == (long)c->clientid) {
352
+			c->dienow = 1;
353
+			found++;
354
+		}
355
+	}
356
+	list_unlock(clients);
357
+	return found;
358
+}
359
+
360
+int control_client_extend(unsigned long clientid, char *chan, time_t expire) {
361
+	int found = 0;
362
+	LNODE *l, *tmp;
363
+	list_lock(clients);
364
+	list_for_each(clients, l, tmp) {
365
+		CLIENT *c = l->data;
366
+		if (c->clientid == clientid && strcmp(chan, c->chan)==0) {
367
+			c->expire = expire;
368
+			found++;
369
+		}
370
+	}
371
+	list_unlock(clients);
372
+	return found;
373
+}
374
+
375
+int netmsg_send(long client_id, char *channel, char *netmsg) {
376
+	int found = 0;
377
+	LNODE *l, *tmp;
378
+	list_lock(clients);
379
+	list_for_each(clients, l, tmp) {
380
+		CLIENT *c = l->data;
381
+		int nsend =	(client_id == -1          && !channel) ||                                     // Send to all clients
382
+					(client_id == -1          && channel && strcmp(channel, c->chan)==0) ||       // Send to all clients on the channel X
383
+					(client_id == (long)c->clientid && !channel) ||                               // Send to client X
384
+					(client_id == (long)c->clientid && channel && strcmp(channel, c->chan)==0);   // Send to client X on the channel X
385
+		if (nsend) {
386
+			strncpy(c->netmsg, netmsg, sizeof(c->netmsg)-1);
387
+			c->netmsg[sizeof(c->netmsg)-1] = 0;
388
+			found++;
389
+		}
390
+	}
391
+	list_unlock(clients);
392
+	return found;
393
+}
394
+
395
+extern char TS_NULL_FRAME[FRAME_PACKET_SIZE];
396
+
397
+char *get_netmsg_packet(const char *msg) {
398
+	if (!strlen(msg) || strlen(msg) > 60)
399
+		return NULL;
400
+	char *ret = (char *)calloc(1, FRAME_PACKET_SIZE);
401
+	memcpy(ret, TS_NULL_FRAME, FRAME_PACKET_SIZE);
402
+	int i;
403
+	for (i=0; i<7; i++) {
404
+		int ofs = i * 188;
405
+		ret[ofs + 60] = 0x78;
406
+		ret[ofs + 61] = 0x55;
407
+		ret[ofs + 62] = 0x58;
408
+		ret[ofs + 63] = 0x78;
409
+		strcpy(ret + ofs + 64, msg);
410
+	}
411
+	return ret;
412
+}
413
+
414
+int is_netmsg_packet(const unsigned char *buf, int bufsize) {
415
+	if (bufsize < 64 || bufsize > FRAME_PACKET_SIZE)
416
+		return 0;
417
+	if (buf[1] != 0x1f && buf[2] != 0xff) // Look only at NULL packets
418
+		return 0;
419
+	// xUXx
420
+	if (buf[60] == 0x78 && buf[61] == 0x55 && buf[62] == 0x58 && buf[63] == 0x78)
421
+		return 1;
422
+	return 0;
423
+}

+ 160
- 0
data.h View File

1
+/* tsiproxy data functions */
2
+#ifndef DATA_H
3
+# define DATA_H
4
+
5
+#include <netdb.h> // for uint32_t
6
+#include <sys/resource.h> // for rlim_t
7
+#include <sys/utsname.h> // for utsdata
8
+
9
+#include "libfuncs/queue.h"
10
+#include "libfuncs/list.h"
11
+
12
+typedef enum { udp_sock, tcp_sock } channel_source;
13
+
14
+typedef struct {
15
+	int stats :1,		/* S - STATS  | allow access to stats and info server pages /used by data collection tools/ */
16
+	    reconf:1,		/* F - RECONF | allow server reconfiguration request /used for sys admins/ (ex: http://XXX/{chanconf,netconf}) */
17
+		access:1;		/* A - ACCESS | Allow access from this network (used for bg networks) */
18
+} acl_t;
19
+
20
+typedef struct {
21
+	uint32_t net;
22
+	uint32_t mask;
23
+	acl_t acl;
24
+} NETWORK;
25
+
26
+typedef struct {
27
+	rlim_t rlim_cur;   /* Soft limit */
28
+	rlim_t rlim_max;   /* Hard limit (ceiling for rlim_cur) */
29
+} RLIMIT;
30
+
31
+typedef struct  {
32
+	int	  fno;
33
+	char  *chan;
34
+	char  *IP;
35
+	char  *agent;
36
+	unsigned long clientid;	//from the web
37
+	time_t expire;			//end of the validity of the signature
38
+	time_t start;
39
+	time_t ts;				//timestamp for staled connections
40
+	unsigned pos;			
41
+	unsigned long long traffic_out; // How much traffic has been send to this client
42
+	uint32_t ip;			// Client ip
43
+	acl_t acl;				//ACL for the IP from netconf
44
+	uint32_t start_pts;		//client started on this global pts 
45
+	uint16_t client_port;	//the udp port that client is listening to
46
+	int 
47
+	    smart_client:1,		// The client has it's own reconnect logic and will be disconnected on channel reconnect (child servers)
48
+	    is_child_server:1,	// The client is really a child server (have iptvd user agent)
49
+	    headers_sent:1,     // headers have been sent
50
+	    logged:1,			//connection is logged
51
+	    data_send:1,		//The client has received at least one data packets
52
+	    dienow:1,			//When this is set the client must be disconnected immedietly
53
+	    stopping:1;			//The client is being stopped. This flag prevents double client_stop entry
54
+	char netmsg[48];
55
+} CLIENT;
56
+
57
+typedef struct {
58
+	channel_source sproto;
59
+	char *proto;
60
+	char *host;
61
+	char *path;
62
+	unsigned int port;
63
+} CHANSRC;
64
+
65
+#define MAX_CHANNEL_SOURCES 8
66
+
67
+typedef struct {
68
+	char *name;
69
+	char *source; /* Full source url */
70
+	char *sources[MAX_CHANNEL_SOURCES];
71
+	uint8_t num_src;
72
+	uint8_t curr_src;
73
+} CHANNEL;
74
+
75
+typedef struct {
76
+	char  *name;
77
+	QUEUE *queue;			/* Waiting clients */
78
+	LIST *clients;			/* List of clients connected to this restreamer */
79
+	CHANNEL *channel;
80
+	int sock;
81
+	struct sockaddr_in sockname;
82
+	int reconnect;			/* Set to 1 to force proxy reconnect */
83
+	int connected;			/* It's set to 1 when proxy is connected and serving clients */
84
+	int dienow;				/* Stop serving clients and exit now */
85
+	int freechannel;		/* Free channel data on object free (this is used in chanconf) */
86
+	time_t started;
87
+	int cookie;				/* Used in chanconf to determine if the restreamer is alrady checked */
88
+	unsigned long connects;	/* How many times the proxy has connected successfully to parent */
89
+	unsigned long served;	/* How much clients has been served by this proxy */
90
+	unsigned long long traffic_in;
91
+	unsigned long long traffic_out;
92
+} RESTREAMER;
93
+
94
+typedef struct {
95
+	unsigned long clients;				/* Number of clients requested channel and stay connected at least 3 seconds */
96
+	unsigned long child_servers;		/* Number of child servers */
97
+	unsigned long clients_all;			/* Number of clients + child servers + clients not watched at least 3 seconds */
98
+	unsigned long clients_current;		/* Number of current clients */
99
+	unsigned long clients_gone;			/* Number of stoped clients since last "readinfo" command was issued */
100
+	unsigned long errors;				/* Number of error videos served */
101
+	unsigned long long traffic_in;		/* How much traffic was downloaded from parent server */
102
+	unsigned long long traffic_out;		/* How much traffic was served to clients and sub restreamers */
103
+	time_t start_ts;					/* When the server was started */
104
+	struct utsname utsdata;
105
+} STATS;
106
+
107
+typedef struct {
108
+	int client;
109
+	int file;
110
+	char *msg;
111
+	char *content_type;
112
+} ERRMSG;
113
+
114
+channel_source get_sproto(char *url);
115
+
116
+CHANSRC *init_chansrc(char *url);
117
+void free_chansrc(CHANSRC *url);
118
+int is_valid_url(char *url);
119
+
120
+void add_channel_source(CHANNEL *c, char *src);
121
+void next_channel_source(CHANNEL *c);
122
+void set_channel_source(CHANNEL *c, uint8_t src_id);
123
+
124
+CHANNEL * new_channel(char *id, char *source);
125
+void free_channel(CHANNEL *c);
126
+int channel_search(LIST *channels_list, char *name, CHANNEL **found_channel);
127
+
128
+NETWORK * new_network(uint32_t net, uint32_t mask, acl_t acl);
129
+void free_network(NETWORK *n);
130
+int acl_search(LIST *acl_list, uint32_t search_ip, acl_t *found_acl);
131
+
132
+RESTREAMER * new_restreamer(const char *name, CHANNEL *channel);
133
+void free_restreamer(RESTREAMER *r);
134
+
135
+void	restreamer_stop_all		(LIST *proxys);
136
+int		restreamer_stop			(LIST *proxys, char *channel);
137
+int		restreamer_reconnect	(LIST *proxys, char *channel);
138
+
139
+int is_ext(char *path, char *ext);
140
+
141
+CLIENT * new_client(int fd, time_t expire, char *chan, char *IP, char *agent, unsigned long clientid, acl_t acl, uint16_t client_port, int smart_client);
142
+void free_client(CLIENT *c);
143
+
144
+void stop_client(CLIENT *c, int socket_shutdown, char mark);
145
+
146
+void stop_client_noshutdown(CLIENT *c);
147
+void stop_client_shutdown_mark(CLIENT *c, char mark);
148
+void stop_client_shutdown(CLIENT *c);
149
+
150
+void client_log_connect(CLIENT *c);
151
+
152
+int control_client_stop(long clientid);
153
+int control_client_extend(unsigned long clientid, char *chan, time_t expire);
154
+
155
+int netmsg_send(long client_id, char *channel, char *netmsg);
156
+
157
+char *get_netmsg_packet(const char *msg);
158
+int is_netmsg_packet(const unsigned char *buf, int bufsize);
159
+
160
+#endif

+ 74
- 0
defines.h View File

1
+#ifndef DEFINES_H
2
+# define DEFINES_H
3
+
4
+#undef DEBUG
5
+//#define DEBUG 1
6
+
7
+#define VERSION_ID "1.00"
8
+#define BUILD_ID GIT_BUILD_ID
9
+
10
+#define SERVER_SIGNATURE "tsiproxy"
11
+#define COPYRIGHT "Copyright (c) 2020 Georgi Chorbadzhiiyski <georgi@unixsol.net>"
12
+
13
+/*
14
+  This define is used to compensate for servers clock drift.
15
+  It allows to watch URLs which otherwise will be active after
16
+  upto URL_START_TOLERANCE seconds in the future. If the server
17
+  clock is more than URL_START_TOLERANCE late or the URL is
18
+  generated too far in the future, the client will get
19
+  not-active video.
20
+*/
21
+#define URL_START_TOLERANCE 180
22
+
23
+/*
24
+  This define is used to set after how many seconds with no
25
+  traffic the client will be disconnected.
26
+*/
27
+#define NO_TRAFFIC_SECONDS 30
28
+
29
+/*
30
+  How many seconds after start of connection NEW message will be logged.
31
+  The check is > NO_LOG_START_SECONDS
32
+*/
33
+#define NO_LOG_START_SECONDS 3
34
+
35
+/*
36
+  How many seconds minimum should LOG{} be emitted.
37
+  The check is > NO_LOG_END_SECONDS
38
+*/
39
+#define NO_LOG_END_SECONDS 3
40
+
41
+/* How much miliseconds to wait for connection to be established with config server */
42
+#define CONFIG_CONNECT_TIMEOUT 10*1000
43
+
44
+#define CONFIG_LOAD_TIMEOUT 60
45
+
46
+
47
+
48
+
49
+/* How much to wait for connection to be established with channel source (miliseconds) */
50
+#define PROXY_CONNECT_TIMEOUT 1000
51
+
52
+/*
53
+	The total time it takes to return no-signal is equal to
54
+		PROXY_CONNECT_RETRIES * PROXY_RETRY_TIMEOUT
55
+*/
56
+
57
+/* How many times to try to connect to channel */
58
+#define PROXY_CONNECT_RETRIES 5
59
+
60
+/* Seconds to sleep between retries (miliseconds) */
61
+#define PROXY_RETRY_TIMEOUT 1000
62
+
63
+#define FRAME_PACKET_SIZE 1316
64
+
65
+#define NEW_DURATION_WEIGHT 0.5
66
+#define TIME_ADJUSTMENT_FACTOR 0.8
67
+#define MAX_PLAYOUT_BUFFER_DURATION 0.1 // (seconds)
68
+#define PCR_PERIOD_VARIATION_RATIO 0.5
69
+
70
+#define unused(x) (void)x
71
+
72
+#define BUF 1536
73
+
74
+#endif

+ 1
- 0
libfuncs

1
+Subproject commit 8fcac8dd4db1e1e382f072a722ef360b8d8b8251

+ 524
- 0
proxy_common.c View File

1
+/* tsiproxy stream proxy */
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <stdarg.h>
5
+#include <unistd.h>
6
+#include <string.h>
7
+#include <signal.h>
8
+#include <netdb.h>
9
+#include <pthread.h>
10
+#include <regex.h>
11
+#include <arpa/inet.h>
12
+#include <netinet/in.h>
13
+#include <sys/utsname.h>
14
+#include <sys/socket.h>
15
+#include <sys/time.h>
16
+#include <sys/types.h>
17
+#include <sys/stat.h>
18
+#include <sys/utsname.h>
19
+#include <sys/resource.h>
20
+
21
+#include <fcntl.h>
22
+#include <errno.h>
23
+#include <poll.h>
24
+
25
+#include "libfuncs/libfuncs.h"
26
+
27
+#include "data.h"
28
+#include "conf.h"
29
+#include "proxy_common.h"
30
+
31
+extern char *server_sig;
32
+extern char *server_ver;
33
+
34
+extern regex_t http_response;
35
+
36
+extern LIST *restreamer, *chanconf;
37
+extern STATS allstats;
38
+
39
+extern CONFIG *config;
40
+extern int keep_running;
41
+
42
+extern void * proxy_ts_stream(void *channel_id);
43
+
44
+void process_client(int clientsock, CLIENT *client) {
45
+	list_lock(chanconf);
46
+
47
+	LNODE *l, *tmp;
48
+	CHANNEL *chan = NULL;
49
+	list_for_each(chanconf, l, tmp) {
50
+		if (strcmp(client->chan, ((CHANNEL *)l->data)->name)==0) {
51
+			chan = l->data;
52
+			break;
53
+		}
54
+	}
55
+
56
+	/* Check if requested channel exists? */
57
+	if (!chan) {
58
+		LOGf("ERROR: No such channel | IP: %s Path: /%s Agent: %s\n", client->IP, client->chan, client->agent);
59
+		send_404_not_found(client->fno);
60
+		stop_client_shutdown(client);
61
+		list_unlock(chanconf);
62
+		return;
63
+	}
64
+
65
+	/* Is there an active restreamer for the requested channel */
66
+	list_lock(restreamer);
67
+	RESTREAMER *r = NULL;
68
+	list_for_each(restreamer, l, tmp) {
69
+		if (strcmp(client->chan, ((RESTREAMER *)l->data)->name)==0) {
70
+			r = l->data;
71
+			break;
72
+		}
73
+	}
74
+	if (r) {
75
+		/* Active restreamer found. Add client to it's queue. Do it here while restreamer is locked otherwise there is a race! */
76
+		queue_add(r->queue, client);
77
+		list_unlock(restreamer);
78
+		list_unlock(chanconf);
79
+		return;
80
+	}
81
+	list_unlock(restreamer);
82
+
83
+	/* No active restreamer found, start new one */
84
+	RESTREAMER *nr = new_restreamer(client->chan, chan);
85
+	queue_add(nr->queue, client);
86
+	list_add(restreamer, nr);
87
+	list_unlock(chanconf);
88
+
89
+	pthread_t newthread;
90
+	LOGf("SPAWN: Restreamer | Channel: %s Source: %s client_fd: %i\n", chan->name, chan->source, clientsock);
91
+	if (pthread_create(&newthread, NULL, &proxy_ts_stream, nr)) {
92
+		log_perror("Error TS proxy creating thread.", errno);
93
+		exit(1);
94
+	}
95
+	pthread_detach(newthread);
96
+}
97
+
98
+static void remove_clients(RESTREAMER *r, const char *text, int error_code) {
99
+	CLIENT *c;
100
+	while ((c=queue_get_nowait(r->queue))) {
101
+		LOGf("NOSIG: fd: %i srv_fd: %i {Op:%s} {Err:%d} | Client: %lu IP: %s Channel: %s Agent: %s\n",
102
+		     c->fno, r->sock, text, error_code,
103
+		     c->clientid, c->IP, c->chan, c->agent);
104
+		// Headers are already sent or the client is using udp, just yank it
105
+		if (c->headers_sent || c->client_port) {
106
+			stop_client_shutdown(c);
107
+		} else {
108
+			char *code = "504 Gateway Timeout";
109
+			char *msg = "no-signal";
110
+			if (error_code >= 300) {
111
+				send_http_response(c->fno, code);
112
+				send_header_textplain(c->fno);
113
+				fdputsf(c->fno, "X-ErrorCode: %d\nX-Error: %s\n\n%s\n", error_code, msg, msg);
114
+			} else {
115
+				send_http_error(c->fno, code, msg);
116
+			}
117
+			stop_client_shutdown(c);
118
+		}
119
+	}
120
+}
121
+
122
+static void remove_smart_clients(RESTREAMER *r) {
123
+	CLIENT *c;
124
+	LNODE *l, *tmp;
125
+	list_for_each(r->clients, l, tmp) {
126
+		c = l->data;
127
+		if (!c->smart_client)
128
+			continue;
129
+		list_del(r->clients, &l);
130
+		if (c->headers_sent || c->client_port) {
131
+			stop_client_shutdown(c);
132
+		} else {
133
+			send_http_error(c->fno, "504 Gateway Timeout", "no-signal");
134
+			stop_client_shutdown(c);
135
+		}
136
+	}
137
+}
138
+
139
+void proxy_close(RESTREAMER *r, int error_code) {
140
+	LOGf("CLOSE: Restreamer | Channel: %s Source: %s IP: %s srv_fd: %i Conn: %ld Serv: %ld TrafIn: %lld TrafOut: %lld\n",
141
+		r->channel->name,
142
+		r->channel->source,
143
+		inet_ntoa(r->sockname.sin_addr),
144
+		r->sock,
145
+		r->connects,
146
+		r->served,
147
+		r->traffic_in,
148
+		r->traffic_out);
149
+	// If there are no clients left, no "Timeout" messages will be logged
150
+	remove_clients(r, "Timeout", error_code);
151
+	list_del_entry(restreamer, r);
152
+	free_restreamer(r);
153
+}
154
+
155
+void proxy_begin_reconnect(RESTREAMER *r) {
156
+	shutdown_fd(&(r->sock));
157
+	next_channel_source(r->channel);
158
+	remove_smart_clients(r);
159
+}
160
+
161
+#define FATAL_ERROR(__no_signal_msg) do \
162
+{ \
163
+	remove_clients(r, __no_signal_msg, *http_code); \
164
+	free_chansrc(src); \
165
+	return -1; \
166
+} while (0)
167
+
168
+/*
169
+	On the last try, send no-signal to clients and exit
170
+	otherwise wait a little bit before trying again
171
+*/
172
+#define DO_RECONNECT do \
173
+{ \
174
+	if (retries == 0) { \
175
+		FATAL_ERROR("Connect"); \
176
+	} else { \
177
+		free_chansrc(src); \
178
+		if (errno != EHOSTUNREACH) /* When host is unreachable there is already a delay of ~4 secs per try so no sleep is needed */ \
179
+			usleep(PROXY_RETRY_TIMEOUT * 1000); \
180
+		return 1; \
181
+	} \
182
+} while(0)
183
+
184
+/*
185
+	Returns:
186
+		-1 = exit thread
187
+		 1 = retry
188
+		 0 = connected ok
189
+*/
190
+int connect_source(RESTREAMER *r, int retries, int readbuflen, int *http_code) {
191
+	CHANSRC *src = init_chansrc(r->channel->source);
192
+	if (!src) {
193
+		LOGf("ERROR: Can't parse channel source | Channel: %s Source: %s\n", r->channel->name, r->channel->source);
194
+		FATAL_ERROR("cant-parse-channel-source");
195
+	}
196
+	r->connected = 0;
197
+	r->reconnect = 0;
198
+
199
+	int active = 1;
200
+	int dret = async_resolve_host(src->host, src->port, &(r->sockname), DNS_RESOLVER_TIMEOUT, &active);
201
+	if (dret != 0) {
202
+		if (dret == 1)
203
+			LOGf("ERROR: Can't resolve host | Channel: %s Source: %s Host: %s\n", r->channel->name, r->channel->source, src->host);
204
+		if (dret == 2)
205
+			LOGf("ERROR: DNS timeout | Channel: %s Source: %s Host: %s\n", r->channel->name, r->channel->source, src->host);
206
+		DO_RECONNECT;
207
+	}
208
+
209
+	char buf[1024];
210
+	*http_code = 0;
211
+	if (src->sproto == tcp_sock) {
212
+		r->sock = socket(PF_INET, SOCK_STREAM, 0);
213
+		if (r->sock < 0) {
214
+			log_perror("connect_source(): Could not create SOCK_STREAM socket.", errno);
215
+			FATAL_ERROR("cant-create-sock-stream-socket");
216
+		}
217
+		LOGf("RCONN: Connecting | Channel: %s Source: %s IP: %s src_fd: %i TriesLeft: %i\n",
218
+			r->channel->name,
219
+			r->channel->source,
220
+			inet_ntoa(r->sockname.sin_addr),
221
+			r->sock,
222
+			retries
223
+		);
224
+		if (do_connect(r->sock, (struct sockaddr *)&(r->sockname), sizeof(r->sockname), PROXY_CONNECT_TIMEOUT) < 0) {
225
+			LOGf("ERROR: Error connecting to %s srv_fd: %i err: %s\n", r->channel->source, r->sock, strerror(errno));
226
+			DO_RECONNECT;
227
+		}
228
+
229
+		snprintf(buf,sizeof(buf)-1, "GET /%s HTTP/1.0\nHost: %s:%u\nUser-Agent: %s %s (%s)\n\n",
230
+		         src->path, src->host, src->port, server_sig, server_ver, config->ident);
231
+		buf[sizeof(buf)-1] = 0;
232
+		allstats.traffic_out += safe_write(r->sock, buf, strlen(buf));
233
+
234
+		char xresponse[128];
235
+		memset(xresponse, 0, sizeof(xresponse));
236
+		memset(buf, 0, sizeof(buf));
237
+		regmatch_t res[4];
238
+		while (fdgetline(r->sock,buf,sizeof(buf)-1)) {
239
+			if (buf[0] == '\n' || buf[0] == '\r')
240
+				break;
241
+			if (strstr(buf,"HTTP/1.") != NULL) {
242
+				if (regexec(&http_response,buf,3,res,0) != REG_NOMATCH) {
243
+					char codestr[4];
244
+					if ((unsigned int)res[1].rm_eo-res[1].rm_so < (unsigned)sizeof(xresponse)) {
245
+						strncpy(xresponse, &buf[res[1].rm_so], res[1].rm_eo-res[1].rm_so);
246
+						xresponse[res[1].rm_eo-res[1].rm_so] = '\0';
247
+						chomp(xresponse);
248
+						strncpy(codestr, &buf[res[2].rm_so], res[2].rm_eo-res[2].rm_so);
249
+						codestr[3] = 0;
250
+						*http_code = atoi(codestr);
251
+					}
252
+				}
253
+			}
254
+			if (*http_code == 504) { // Extract extra error code
255
+				if (strstr(buf, "X-ErrorCode: ") != NULL) {
256
+					*http_code = atoi(buf+13);
257
+					break;
258
+				}
259
+			}
260
+		}
261
+		if (*http_code == 0) { // No valid HTTP response, retry
262
+			LOGf("DEBUG: Server returned not valid HTTP code | srv_fd: %i\n", r->sock);
263
+			DO_RECONNECT;
264
+		}
265
+		if (*http_code == 504) { // No signal, exit
266
+			LOGf("ERROR: Get no-signal for %s from %s on srv_fd: %i\n", r->channel->name, r->channel->source, r->sock);
267
+			FATAL_ERROR("no-signal");
268
+		}
269
+		if (*http_code > 300) { // Unhandled or error codes, exit
270
+			LOGf("ERROR: Get code %i for %s from %s on srv_fd: %i exiting.\n", *http_code, r->channel->name, r->channel->source, r->sock);
271
+			FATAL_ERROR("unhandled-http-code");
272
+		}
273
+		// connected ok, continue
274
+	} else {
275
+		if (!IN_MULTICAST(ntohl(r->sockname.sin_addr.s_addr))) {
276
+			LOGf("ERROR: %s is not multicast address\n", r->channel->source);
277
+			FATAL_ERROR("source-is-not-multicast-address");
278
+		}
279
+		struct ip_mreq mreq;
280
+		struct sockaddr_in receiving_from;
281
+
282
+		r->sock = socket(PF_INET, SOCK_DGRAM, 0);
283
+		if (r->sock < 0) {
284
+			log_perror("play(): Could not create SOCK_DGRAM socket.", errno);
285
+			FATAL_ERROR("cant-create-sock-dgram-socket");
286
+		}
287
+		LOGf("RCONN: Listening on multicast socket %s srv_fd: %i retries left: %i\n", r->channel->source, r->sock, retries);
288
+		int on = 1;
289
+		setsockopt(r->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
290
+		// subscribe to multicast group
291
+		memcpy(&mreq.imr_multiaddr, &(r->sockname.sin_addr), sizeof(struct in_addr));
292
+		mreq.imr_interface.s_addr = htonl(INADDR_ANY);
293
+		if (setsockopt(r->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
294
+			LOGf("ERROR: Failed to add IP membership on %s srv_fd: %i\n", r->channel->source, r->sock);
295
+			FATAL_ERROR("failed-to-add-membership");
296
+		}
297
+		// bind to the socket so data can be read
298
+		memset(&receiving_from, 0, sizeof(receiving_from));
299
+		receiving_from.sin_family = AF_INET;
300
+		receiving_from.sin_addr   = r->sockname.sin_addr;
301
+		receiving_from.sin_port   = htons(src->port);
302
+		if (bind(r->sock, (struct sockaddr *) &receiving_from, sizeof(receiving_from)) < 0) {
303
+			LOGf("ERROR: Failed to bind to %s srv_fd: %i\n", r->channel->source, r->sock);
304
+			FATAL_ERROR("failed-to-bind");
305
+		}
306
+	}
307
+
308
+	if (setsockopt(r->sock, SOL_SOCKET, SO_RCVBUF, (const char *)&readbuflen, sizeof(readbuflen)) < 0)
309
+		log_perror("play(): setsockopt(SO_RCVBUF)", errno);
310
+
311
+	r->connects++;
312
+	r->connected = 1;
313
+
314
+	free_chansrc(src);
315
+	return 0;
316
+}
317
+
318
+
319
+int connect_udp_client(CLIENT *c) {
320
+	int newsock = -1;
321
+	int client_socket = c->fno;
322
+	struct sockaddr_in sockname;
323
+	struct in_addr client_ip;
324
+	inet_aton(c->IP, &client_ip);
325
+	sockname.sin_family = AF_INET;
326
+	sockname.sin_port = htons(c->client_port);
327
+	sockname.sin_addr = client_ip;
328
+	// Create new udp socket to the client
329
+	newsock = socket(PF_INET, SOCK_DGRAM, 0);
330
+	if (newsock < 0) {
331
+		log_perror("process_new_clients(): Could not create SOCK_DGRAM socket.", errno);
332
+		stop_client_noshutdown(c); // ??
333
+		return -1;
334
+	}
335
+	// Connect to the client
336
+	// Connect on udp socket never returns an error
337
+	// The error will be returned on the first send/write
338
+	do_connect(newsock, (struct sockaddr *)&sockname, sizeof(sockname), 1000);
339
+	// Return pls playlist (vlc supports it)
340
+	send_200_ok(c->fno);
341
+	if (c->agent && strstr(c->agent,"VLC")==c->agent) { // VLC support pls playlists
342
+		fdputsf(c->fno, "Content-type: application/pls+xml\n\n[playlist]\nFile1=%s://@:%u\nTitle1=LiveTV\n", "udp", c->client_port);
343
+		// sleep a little bit so VLC can start listening to the udp port
344
+		shutdown_fd(&client_socket);
345
+		usleep(500000);
346
+	} else {
347
+		fdputsf(c->fno, "Content-type: text/plain\n\n%s://@:%u\n", "udp", c->client_port);
348
+		shutdown_fd(&client_socket);
349
+	}
350
+	// Change client socket to the new udp socket
351
+	c->fno = newsock;
352
+	return newsock;
353
+}
354
+
355
+void process_new_clients(RESTREAMER *r, int bufsz, uint32_t start_pts, unsigned int pos) {
356
+	CLIENT *c;
357
+	while ((c=queue_get_nowait(r->queue))) {
358
+		if (c->clientid) {
359
+			int client_exists = 0;
360
+			{
361
+				LNODE *l, *tmp;
362
+				CLIENT *n = NULL;
363
+				list_lock(r->clients);
364
+				list_for_each_reverse(r->clients, l, tmp) {
365
+					if (c->clientid == ((CLIENT *)l->data)->clientid) {
366
+						n = l->data;
367
+						break;
368
+					}
369
+				}
370
+				// if the same client is already watching the channel but from different IP
371
+				// or the same client is already watching the channel on the same port
372
+				if (n && (strcmp(n->IP,c->IP)!=0 || (n->client_port && n->client_port==c->client_port)))
373
+					client_exists = 1;
374
+				list_unlock(r->clients);
375
+			}
376
+			if (client_exists) {
377
+				LOGf("2ND  : fd: %i | Client: %lu IP: %s Channel: %s Agent: %s\n", 
378
+					c->fno, c->clientid, c->IP, c->chan, c->agent);
379
+				send_400_bad_request(c->fno, "already-watching-the-channel");
380
+				stop_client_shutdown(c);
381
+				continue;
382
+			}
383
+		}
384
+		// Connect with udp to the client, closing incomming http req socket
385
+		if (c->client_port && connect_udp_client(c) == -1) {
386
+			continue;
387
+		}
388
+		if (setsockopt(c->fno, SOL_SOCKET, SO_SNDBUF, (const char *) &bufsz,sizeof(bufsz))<0)
389
+			log_perror("play(): setsockopt(SO_SNDBUF)", errno);
390
+		c->pos = pos;
391
+		c->start_pts = start_pts;
392
+		list_add(r->clients, c);
393
+	}
394
+}
395
+
396
+int shout(RESTREAMER *r, char *buf, const size_t bufsz, const size_t framelen, unsigned int pos, const char *content_type, char *header, unsigned int header_len) {
397
+	CLIENT *n;
398
+	int num_clients=0, nfds=0;
399
+	struct timeval tv;
400
+	fd_set wfds,efds,rfds;
401
+	time_t now = time(NULL);
402
+
403
+	if (r->clients==NULL || r->clients->items == 0)
404
+		return 0;
405
+
406
+	FD_ZERO(&wfds);
407
+	FD_ZERO(&rfds);
408
+	FD_ZERO(&efds);
409
+
410
+	LNODE *l, *tmp;
411
+	list_for_each_reverse(r->clients, l, tmp) {
412
+		n = l->data;
413
+		/* EXPR: No traffic in XX seconds. Kill the client. X seconds have passed and 0 traffic send - kill */
414
+		if ((now-n->ts >= NO_TRAFFIC_SECONDS) ||
415
+		    (n->traffic_out == 0 && now-n->start >= NO_TRAFFIC_SECONDS))
416
+		{
417
+			n->start   += NO_TRAFFIC_SECONDS;
418
+			stop_client_shutdown_mark(n, 'x');
419
+			list_del(r->clients, &l);
420
+			continue;
421
+		}
422
+		FD_SET(n->fno,&wfds);
423
+		FD_SET(n->fno,&efds);
424
+		FD_SET(n->fno,&rfds);
425
+		if (n->fno>nfds) nfds=n->fno;
426
+		num_clients++;
427
+
428
+		if (!n->logged) {
429
+			client_log_connect(n);
430
+			r->served++;
431
+		}
432
+	}
433
+	tv.tv_sec = 0;
434
+	tv.tv_usec= 0;
435
+
436
+	if (select(nfds+1,&rfds,&wfds,&efds,&tv)>0) {
437
+		list_for_each_reverse(r->clients, l, tmp) {
438
+			n = l->data;
439
+			if ((FD_ISSET(n->fno,&rfds)) || (FD_ISSET(n->fno,&efds)) || // closed connection
440
+				((n->expire > 0) && (now > n->expire)) || // expired session
441
+				n->dienow) // Forcefully stopped client
442
+			{
443
+				stop_client_shutdown(n);
444
+				list_del(r->clients, &l);
445
+				num_clients--;
446
+				continue;
447
+			}
448
+			// OK, send him the stream if is ready
449
+			if ((buf!=NULL)&&(FD_ISSET(n->fno,&wfds))){
450
+				// Sent headers to the client (http client)
451
+				if (!n->client_port && !n->headers_sent) {
452
+					n->headers_sent = 1;
453
+					send_200_ok(n->fno); // Standart http client response
454
+					fdputsf(n->fno, "%s", content_type);
455
+					if (header != NULL && header_len > 0) {
456
+						allstats.traffic_out += safe_write(n->fno, header, header_len);
457
+					}
458
+				}
459
+				int i,j;
460
+				// When in mpeg mode the first packet is always 1316 also the client is at current buffer position
461
+				if (!n->data_send && framelen == FRAME_PACKET_SIZE) {
462
+					i = framelen;
463
+					n->data_send = 1;
464
+					if (config->disable_burst) {
465
+						if (pos >= FRAME_PACKET_SIZE)
466
+							n->pos = pos - FRAME_PACKET_SIZE;
467
+						else
468
+							n->pos = pos;
469
+					}
470
+				}
471
+				if (pos > n->pos) {
472
+					i = pos - n->pos;
473
+				} else {
474
+					i = bufsz * framelen - n->pos;
475
+				}
476
+				// Sent it
477
+				j = safe_write(n->fno, buf+n->pos, i);
478
+				if (j >= 0) {
479
+					/* 0 bytes written is not an error per se, but here we threat
480
+					   it special, because there are buggy clients that connect and
481
+					   start reading 0 bytes. If we do not set n->ts these clients
482
+					   will be disconnected when NO_TRAFFIC_SECONDS pass */
483
+					if (j > 0) {
484
+						allstats.traffic_out += j;
485
+						r->traffic_out += j;
486
+						n->traffic_out += j;
487
+						n->pos += j;
488
+						n->ts = time(NULL);
489
+					}
490
+					if (n->pos == bufsz * framelen)
491
+						n->pos = 0;
492
+					// Send netmsg
493
+					if (framelen == FRAME_PACKET_SIZE && n->netmsg[0]) {
494
+						char *netmsg = get_netmsg_packet(n->netmsg);
495
+						if (netmsg) {
496
+							safe_write(n->fno, netmsg, FRAME_PACKET_SIZE);
497
+							n->netmsg[0] = 0;
498
+							FREE(netmsg);
499
+						}
500
+					}
501
+				} else {
502
+					stop_client_shutdown(n);
503
+					list_del(r->clients, &l);
504
+					num_clients--;
505
+				}
506
+			}
507
+		}
508
+	}
509
+	return num_clients;
510
+}
511
+
512
+int check_restreamer_state(RESTREAMER *r) {
513
+	if (!keep_running)
514
+		return 2;
515
+	if (r->dienow) {
516
+		LOGf("RESTR: Forced disconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
517
+		return 2;
518
+	}
519
+	if (r->reconnect) {
520
+		LOGf("RESTR: Forced reconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
521
+		return 1;
522
+	}
523
+	return 0;
524
+}

+ 21
- 0
proxy_common.h View File

1
+/* tsiproxy stream proxy */
2
+#ifndef PROXY_COMMON_H
3
+# define PROXY_COMMON_H
4
+
5
+#include "data.h"
6
+
7
+void process_client(int clientsock, CLIENT *client);
8
+
9
+void proxy_begin_reconnect(RESTREAMER *r);
10
+
11
+void proxy_close(RESTREAMER *r, int error_code);
12
+
13
+int connect_source(RESTREAMER *r, int retries, int readbuflen, int *http_code);
14
+
15
+void process_new_clients(RESTREAMER *r, int bufsz, uint32_t start_pts, unsigned int pos);
16
+
17
+int shout(RESTREAMER *r, char *buf, const size_t bufsz, const size_t framelen, unsigned int pos, const char *content_type, char *header, unsigned int header_len);
18
+
19
+int check_restreamer_state(RESTREAMER *r);
20
+
21
+#endif

+ 239
- 0
proxy_ts.c View File

1
+/* tsiproxy mpeg transport stream proxy */
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <stdarg.h>
5
+#include <unistd.h>
6
+#include <string.h>
7
+#include <signal.h>
8
+#include <netdb.h>
9
+#include <pthread.h>
10
+#include <arpa/inet.h>
11
+#include <netinet/in.h>
12
+#include <sys/socket.h>
13
+#include <sys/time.h>
14
+#include <sys/types.h>
15
+
16
+#include <fcntl.h>
17
+#include <errno.h>
18
+
19
+#include "libfuncs/libfuncs.h"
20
+
21
+#include "data.h"
22
+#include "conf.h"
23
+#include "proxy_common.h"
24
+
25
+extern LIST *restreamer;
26
+extern STATS allstats;
27
+extern CONFIG *config;
28
+
29
+extern char TS_NULL_FRAME[FRAME_PACKET_SIZE];
30
+extern int keep_running;
31
+
32
+#define MAX_ZERO_READS 3
33
+
34
+#define CONNECT_RETRIES 2
35
+
36
+/*         Start: 3 seconds on connect */
37
+/* In connection: Max UDP timeout == 3 seconds (read) + 2 seconds (connect) == 5 seconds */
38
+#define UDP_READ_RETRIES 3
39
+#define UDP_READ_TIMEOUT 1000
40
+
41
+/*         Start: 1/4 seconds on connect */
42
+/* In connection: Max TCP timeout == 5 seconds (read) + 2 seconds (connect)             == 7 seconds */
43
+/* In connection: Max TCP timeout == 5 seconds (read) + 8 seconds (connect, host unrch) == 13 seconds */
44
+#define TCP_READ_RETRIES 5
45
+#define TCP_READ_TIMEOUT 1000
46
+
47
+/*
48
+	Returns:
49
+		0 = synced ok
50
+		1 = not synced, reconnect
51
+*/
52
+int mpeg_sync(int proxysock, char *channel, channel_source source_proto) {
53
+	time_t sync_start = time(NULL);
54
+	unsigned int sync_packets = 0;
55
+	unsigned int read_bytes = 0;
56
+	char syncframe[188];
57
+
58
+	if (!config->ts_sync)
59
+		return 0;
60
+
61
+	int _timeout = TCP_READ_TIMEOUT;
62
+	int _retries = TCP_READ_RETRIES;
63
+	if (source_proto == udp_sock) {
64
+		_timeout = UDP_READ_TIMEOUT;
65
+		_retries = UDP_READ_RETRIES;
66
+	}
67
+	do {
68
+resync:
69
+		if (!keep_running)
70
+			return 1;
71
+		if (fdread_ex(proxysock, syncframe, 1, _timeout, _retries, 1) != 1) {
72
+			LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
73
+			return 1; // reconnect
74
+		}
75
+		// LOGf("DEBUG:     Read 0x%02x Offset %u Sync: %u\n", (uint8_t)syncframe[0], read_bytes, sync_packets);
76
+		read_bytes++;
77
+		if (syncframe[0] == 0x47) {
78
+			ssize_t rdsz = fdread_ex(proxysock, syncframe, 188-1, _timeout, _retries, 1);
79
+			if (rdsz != 188-1) {
80
+				LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
81
+				return 1; // reconnect
82
+			}
83
+			read_bytes += 188-1;
84
+			if (++sync_packets == 7) // sync 7 packets
85
+				break;
86
+			goto resync;
87
+		} else {
88
+			sync_packets = 0;
89
+		}
90
+		if (read_bytes > FRAME_PACKET_SIZE) { // Can't sync in 1316 bytes
91
+			LOGf("DEBUG: Can't sync after %d bytes | Channel: %s\n", FRAME_PACKET_SIZE, channel);
92
+			return 1; // reconnect
93
+		}
94
+		if (sync_start+2 <= time(NULL)) { // Do not sync in two seconds
95
+			LOGf("DEBUG: Timeout while syncing (read %u bytes) | Channel: %s\n", read_bytes, channel);
96
+			return 1; // reconnect
97
+		}
98
+	} while (1);
99
+	LOGf("SYNC : TS synced after %u bytes | Channel: %s\n", read_bytes-FRAME_PACKET_SIZE, channel);
100
+	return 0;
101
+}
102
+
103
+void * proxy_ts_stream(void *self) {
104
+	RESTREAMER *r = self;
105
+	int retries = CONNECT_RETRIES;
106
+	unsigned int pos = 0;
107
+	int first_connect = 1;
108
+
109
+	signal(SIGPIPE, SIG_IGN);
110
+
111
+#define BUFFER_SIZE (FRAME_PACKET_SIZE * BUF)
112
+
113
+	char buff[BUFFER_SIZE];
114
+	char *buff_end = buff + BUFFER_SIZE;
115
+
116
+	int http_code = 0;
117
+	while (retries--) {
118
+		int result = connect_source(self, retries, BUFFER_SIZE, &http_code);
119
+		if (result > 0) {
120
+			if (!first_connect) {
121
+				goto RECONNECT;
122
+			} else {
123
+				goto EXIT_THREAD;
124
+			}
125
+		}
126
+		if (result < 0)
127
+			goto EXIT_THREAD;
128
+
129
+		switch (check_restreamer_state(r)) {
130
+			case 1: goto RECONNECT;		// r->reconnect is on
131
+			case 2: goto QUIT;			// r->dienow is on
132
+		}
133
+
134
+		channel_source sproto = get_sproto(r->channel->source);
135
+
136
+		int mpgsync = mpeg_sync(r->sock, r->channel->name, sproto);
137
+		if (mpgsync == 1 && first_connect) { // Timeout
138
+			if (sproto == udp_sock) {
139
+				goto RECONNECT;
140
+			} else {
141
+				goto EXIT_THREAD;
142
+			}
143
+		}
144
+
145
+
146
+		char *p = buff;
147
+		pos = 0;
148
+		ssize_t readen = 0;
149
+		int max_zero_reads = MAX_ZERO_READS;
150
+		for (;;) {
151
+			switch (check_restreamer_state(r)) {
152
+				case 1: goto RECONNECT;		// r->reconnect is on
153
+				case 2: goto QUIT;			// r->dienow is on
154
+			}
155
+
156
+			if (sproto == tcp_sock) {
157
+				readen = fdread_ex(r->sock, p, FRAME_PACKET_SIZE, TCP_READ_TIMEOUT, TCP_READ_RETRIES, 1);
158
+			} else {
159
+				int read_so_far = 0;
160
+				int buffer_left;
161
+AGAIN:
162
+				buffer_left = buff_end - (p + read_so_far);
163
+				if (buffer_left >= FRAME_PACKET_SIZE) { // There is enough size for one frame in the buffer
164
+					readen = fdread_ex(r->sock, p+read_so_far, FRAME_PACKET_SIZE, UDP_READ_TIMEOUT, UDP_READ_RETRIES, 0);
165
+					if (readen > 0) {
166
+						read_so_far += readen;
167
+						if (read_so_far < FRAME_PACKET_SIZE)
168
+							goto AGAIN;
169
+						readen = read_so_far;
170
+						buffer_left = buff_end - (p + read_so_far);
171
+						if (buffer_left < FRAME_PACKET_SIZE) {
172
+							memcpy(p+read_so_far, TS_NULL_FRAME, buffer_left);
173
+						}
174
+					}
175
+				} else { // Not enough buffer space, wait a bit so the buffer can wrap around
176
+					if (buffer_left > 0) {
177
+						//LOGf("no read : only %d bytes left to the end of buffer, pos:%d/%d\n", buffer_left, pos, BUF);
178
+						readen = FRAME_PACKET_SIZE; // Oh, yeah we have that many bytes already read
179
+						if (pos+1 == BUF) { // Buffer will wrap now
180
+							//LOGf("Reached buffer end correcting last frame\n");
181
+							p -= FRAME_PACKET_SIZE - buffer_left; // That last packet must be sended now
182
+						} else {
183
+							p -= readen; // Prevent p from moving further (it will be added to bellow)
184
+						}
185
+					}
186
+				}
187
+			}
188
+			if (readen == -1) { // timeout
189
+				if (first_connect || sproto == udp_sock)
190
+					goto EXIT_THREAD;
191
+				break;
192
+			}
193
+			if (readen == 0) { // ho, hum, wtf is going on here?
194
+				if (--max_zero_reads == 0) {
195
+					LOGf("RESTR: %d zero reads on srv_fd: %i | Channel: %s Source: %s\n", MAX_ZERO_READS, r->sock, r->channel->name, r->channel->source);
196
+					break;
197
+				}
198
+				continue;
199
+			}
200
+
201
+			max_zero_reads = MAX_ZERO_READS;
202
+			allstats.traffic_in += readen;
203
+			r->traffic_in += readen;
204
+
205
+			// Fill short frame with NULL packets
206
+			if (readen < FRAME_PACKET_SIZE) {
207
+				LOGf("DEBUG: Short read (%d) on retreamer srv_fd: %i | Channel: %s\n", (int)readen, r->sock, r->channel->name);
208
+				memcpy(p+readen, TS_NULL_FRAME+readen, FRAME_PACKET_SIZE - readen);
209
+				readen = FRAME_PACKET_SIZE;
210
+			}
211
+
212
+			process_new_clients(r, BUFFER_SIZE, 0, 0);
213
+			pos++;
214
+			p += readen;
215
+			if (pos == BUF) {
216
+				pos = 0;
217
+				p=buff;
218
+			}
219
+
220
+			int num_clients = shout(r, buff, BUF, FRAME_PACKET_SIZE, pos*FRAME_PACKET_SIZE, "Content-Type: video/mpeg\n\n", NULL, 0);
221
+			if (!num_clients)
222
+				goto QUIT;
223
+			first_connect = 0;
224
+			retries = CONNECT_RETRIES;
225
+		}
226
+		LOGf("DEBUG: fdread timeout restreamer srv_fd: %i | Channel: %s\n", r->sock, r->channel->name);
227
+RECONNECT:
228
+		if (!keep_running)
229
+			goto QUIT;
230
+		proxy_begin_reconnect(r);
231
+		if (r->clients && r->clients->items)
232
+			continue;
233
+QUIT:
234
+		break;
235
+	}
236
+EXIT_THREAD:
237
+	proxy_close(r, http_code);
238
+	return 0;
239
+}

+ 14
- 0
request.h View File

1
+/* tsiproxy mpeg transport stream proxy */
2
+#ifndef REQUEST_H
3
+# define REQUEST_H
4
+
5
+#include <sys/time.h>
6
+
7
+typedef struct req_info {
8
+	int clientsock;
9
+	struct sockaddr_in client;
10
+} request_info;
11
+
12
+void process_request(void *in_req); /* Uses struct req_info */
13
+
14
+#endif

+ 235
- 0
request_tsiproxy.c View File

1
+/* tsiproxy request handling */
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <stdarg.h>
5
+#include <unistd.h>
6
+#include <string.h>
7
+#include <signal.h>
8
+#include <netdb.h>
9
+#include <pthread.h>
10
+#include <regex.h>
11
+#include <arpa/inet.h>
12
+#include <netinet/in.h>
13
+#include <sys/socket.h>
14
+#include <sys/time.h>
15
+#include <sys/types.h>
16
+
17
+#include <fcntl.h>
18
+#include <errno.h>
19
+
20
+#include "libfuncs/libfuncs.h"
21
+
22
+#include "data.h"
23
+#include "conf.h"
24
+#include "commands.h"
25
+#include "request.h"
26
+#include "proxy_common.h"
27
+
28
+extern LIST *netconf;
29
+
30
+extern regex_t request_get;
31
+extern regex_t request_authmatch;
32
+
33
+extern CONFIG *config;
34
+extern STATS allstats;
35
+
36
+#define FREE_DATA       { FREE(path); FREE(IP); FREE(agent); FREE(buf); }
37
+#define NEXT_CLIENT     { FREE_DATA; pthread_exit(0); }
38
+#define SHUTDOWN_CLIENT { FREE_DATA; shutdown_fd(&clientsock); pthread_exit(0); }
39
+#define BUF_SIZE        1024
40
+
41
+void process_request(void *in_req) {
42
+	request_info *req = (request_info *)in_req;
43
+	regmatch_t res[8];
44
+	uint32_t src_ip;
45
+	char *path=NULL, *IP=NULL, *agent=NULL, *buf=NULL;
46
+	unsigned long client_id = 0;
47
+	int client_udp_port = 0;
48
+	int smart_client = 0;
49
+	int clientsock = req->clientsock;
50
+	struct sockaddr_in client = req->client;
51
+	time_t expire = -1;
52
+	acl_t acl;
53
+	FREE(req);
54
+
55
+	buf = calloc(1, BUF_SIZE);
56
+	if (!buf) {
57
+		log_perror("Can't allocate buffer", errno);
58
+		SHUTDOWN_CLIENT;
59
+	}
60
+
61
+	IP=strdup(inet_ntoa(client.sin_addr));
62
+	src_ip = client.sin_addr.s_addr;
63
+
64
+	if (fdgetline(clientsock, buf, BUF_SIZE-1)<=0) {
65
+		SHUTDOWN_CLIENT;
66
+	}
67
+
68
+	if (regexec(&request_get,buf,2,res,0)==REG_NOMATCH) {
69
+		send_501_not_implemented(clientsock);
70
+		SHUTDOWN_CLIENT;
71
+	}
72
+
73
+	buf[res[1].rm_eo]=0;
74
+	chomp(buf+res[1].rm_so);
75
+	if (buf[res[1].rm_eo-1]=='/') buf[res[1].rm_eo-1]=0;
76
+	path = strdup(buf+res[1].rm_so);
77
+	int plen = strlen(path);
78
+	{
79
+		int k = 0;
80
+		int qmarkpos = 0;
81
+		int esc_7e = 2; // max replaces
82
+		int esc_2b = 32; // max replaces
83
+		int esc_2c = 8; // max replaces
84
+		while (k < plen) {
85
+			/* Fix escaped symbols only when it's safe */
86
+			if (path[k]=='%' && k+2 <= plen) {
87
+				int mv = 0;
88
+				if (esc_7e >= 0 && path[k+1]=='7' && (path[k+2]=='E' || path[k+2]=='e')) { /* %7E -> ~ */
89
+					path[k] = '~';
90
+					esc_7e--;
91
+					mv = 1;
92
+				}
93
+				if (esc_2b >= 0 && path[k+1]=='2' && (path[k+2]=='B' || path[k+2]=='b')) { /* %2B -> + */
94
+					path[k] = '+';
95
+					esc_2b--;
96
+					mv = 1;
97
+				}
98
+				if (esc_2c >= 0 && path[k+1]=='2' && (path[k+2]=='C' || path[k+2]=='c')) { /* %2C -> , */
99
+					path[k] = ',';
100
+					esc_2c--;
101
+					mv = 1;
102
+				}
103
+				if (mv) {
104
+					memmove(&path[k+1], &path[k+3], plen-(k+2));
105
+					plen -= 2;
106
+				}
107
+			}
108
+			/* Bugfix for Windows Media Player. If path have // sequence
109
+			   (in base64 it is happening) WMP send just one / thus invalidating signature
110
+			   To fix this, URL generator replaces // with /_ so now we change _ back to / */
111
+			if (path[k] == '_')
112
+				path[k] = '/';
113
+			/* Some shitty player replaces // with /~ */
114
+			if (path[k] == '~')
115
+				path[k] = '/';
116
+			/* Leave only "safe" characters */
117
+			if (path[k] < '*' || path[k] > 'z' || path[k]=='`')
118
+				path[k] = '*';
119
+			if (path[k] == '?')
120
+				qmarkpos = k;
121
+			k++;
122
+		}
123
+		if (qmarkpos && qmarkpos < plen) {
124
+			path[qmarkpos] = 0;
125
+			client_udp_port = atoi(path+qmarkpos+1);
126
+		}
127
+	}
128
+	while (fdgetline(clientsock, buf, BUF_SIZE-1) > 0) {
129
+		if (buf[0] == '\n' || buf[0] == '\r') // End of headers
130
+			break;
131
+		if (strstr(buf, "X-Smart-Client: yes")==buf) {
132
+			smart_client = 1;
133
+		}
134
+		if (memcmp(buf,"User-Agent: ",12)==0 && strlen(buf) > 12) {
135
+			unsigned int k = 0;
136
+			agent=chomp(strdup(buf+12));
137
+			while(k<strlen(agent)) {
138
+				if (agent[k] < 32 || agent[k] > 122 || agent[k]==96) /* Do not allow ( < && > z && ` */
139
+					agent[k]=' ';
140
+				k++;
141
+			}
142
+		}
143
+	}
144
+
145
+	int pathlen = strlen(path);
146
+	if (!pathlen) {
147
+		send_302_redirect(clientsock, "https://georgi.unixsol.org/");
148
+		SHUTDOWN_CLIENT;
149
+	}
150
+
151
+	int acl_found = acl_search(netconf, client.sin_addr.s_addr, &acl);
152
+
153
+	if (!acl_found) {
154
+		LOGf("DENY : Access denied | IP: %s Path: /%s Agent: %s\n", IP, path, agent);
155
+		send_403_forbidden_msg(clientsock, "access-denied");
156
+		SHUTDOWN_CLIENT;
157
+	}
158
+
159
+	if (strncmp(path,"ping",4)==0) {
160
+		run_ping(clientsock, acl, src_ip, IP);
161
+		SHUTDOWN_CLIENT;
162
+	}
163
+	if (strncmp(path,"stats",5)==0) {
164
+		show_stats(clientsock, acl, IP);
165
+		SHUTDOWN_CLIENT;
166
+	}
167
+	if (strncmp(path,"info",4)==0) {
168
+		show_info(clientsock, acl, IP);
169
+		SHUTDOWN_CLIENT;
170
+	}
171
+	if (strncmp(path,"readinfo",8)==0) {
172
+		read_info(clientsock, acl, IP);
173
+		SHUTDOWN_CLIENT;
174
+	}
175
+	if (strncmp(path,"netconf",7)==0) {
176
+		run_netconf(clientsock, acl, IP);
177
+		SHUTDOWN_CLIENT;
178
+	}
179
+	if (strncmp(path,"chanconf",8)==0) {
180
+		run_chanconf(clientsock, acl, IP);
181
+		SHUTDOWN_CLIENT;
182
+	}
183
+	if (strncmp(path,"getnetconf",10)==0) {
184
+		run_getnetconf(clientsock, acl, IP);
185
+		SHUTDOWN_CLIENT;
186
+	}
187
+	if (strncmp(path,"getchanconf",10)==0) {
188
+		run_getchanconf(clientsock, acl, IP);
189
+		SHUTDOWN_CLIENT;
190
+	}
191
+	if (strncmp(path,"livecheck",9)==0) {
192
+		run_livecheck(clientsock, acl, IP);
193
+		SHUTDOWN_CLIENT;
194
+	}
195
+	if (strncmp(path,"channels",8)==0) {
196
+		run_channels(clientsock, acl, IP);
197
+		SHUTDOWN_CLIENT;
198
+	}
199
+
200
+	if (!is_ext(path, ".asf") && !is_ext(path, ".mpg") && !is_ext(path, ".ts")) {
201
+		if (!is_ext(path, ".ico") &&	// favicon.ico 
202
+		    !is_ext(path, ".txt") &&	// robots.txt 
203
+		    !is_ext(path, ".asx") &&	// Playlists 
204
+		    !is_ext(path, ".m3u") &&	// Playlists 
205
+		    !is_ext(path, ".smi") &&	// Subtitles 
206
+		    !is_ext(path, ".ass") &&
207
+		    !is_ext(path, ".wse") &&
208
+		    !is_ext(path, ".idx") &&
209
+		    !is_ext(path, ".psb") &&
210
+		    !is_ext(path, ".srt") &&
211
+		    !is_ext(path, ".ssa") &&
212
+		    !is_ext(path, ".sub") &&
213
+		    !is_ext(path, ".ASF") &&	// some crap player desides to UPPERCASE the request
214
+		    !is_ext(path, ".wmv") &&	// No wmv files here
215
+		    !is_ext(path, "atus") &&	// apstatus 
216
+		    !is_ext(path, ".exe") &&	// ShitZ 
217
+		    !is_ext(path, ".php") &&
218
+		    !is_ext(path, ".pl") &&
219
+		    !is_ext(path, ".dll"))
220
+		{
221
+			LOGf("404  : File not found | IP: %s Path: /%s Agent: %s\n", IP, path, agent);
222
+		}
223
+		send_404_not_found(clientsock);
224
+		SHUTDOWN_CLIENT;
225
+	}
226
+
227
+	if (config_redirect_enabled(config, clientsock, path)) {
228
+		SHUTDOWN_CLIENT;
229
+	}
230
+
231
+	CLIENT *c = new_client(clientsock, expire, path, IP, agent, client_id, acl, client_udp_port, smart_client);
232
+	process_client(clientsock, c);
233
+
234
+	NEXT_CLIENT;
235
+}

+ 255
- 0
tsiproxy.c View File

1
+/* tsiproxy - mpeg transport stream proxy */
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <unistd.h>
5
+#include <string.h>
6
+#include <pthread.h>
7
+#include <errno.h>
8
+#include <signal.h>
9
+#include <regex.h>
10
+#include <arpa/inet.h>
11
+#include <netinet/in.h>
12
+#include <sys/types.h>
13
+#include <sys/socket.h>
14
+
15
+#include "libfuncs/libfuncs.h"
16
+
17
+#include "data.h"
18
+#include "conf.h"
19
+#include "request.h"
20
+
21
+char *server_sig = SERVER_SIGNATURE;
22
+char *server_ver = VERSION_ID;
23
+
24
+int keep_running = 1;
25
+int rcvsig = 0;
26
+int clean_on_exit = 0;
27
+
28
+CONFIG *config;
29
+
30
+regex_t request_get;
31
+regex_t http_response;
32
+
33
+STATS allstats;
34
+
35
+LIST *netconf = NULL;
36
+LIST *clients = NULL;
37
+LIST *chanconf = NULL;
38
+LIST *restreamer = NULL;
39
+
40
+char TS_NULL_FRAME[FRAME_PACKET_SIZE];
41
+
42
+static int usage_shown = 0;
43
+
44
+static void show_usage(void) {
45
+	if (usage_shown)
46
+		return;
47
+	puts("Identification:");
48
+	puts("");
49
+	puts("    -i ident            Streamer identification. Must be formated as PROVIDER/STREAMER");
50
+	puts("    -c <channels_file>  Channel definitions file");
51
+	puts("    -n <networks_file>  Network definitions file");
52
+	puts("");
53
+	puts("Server settings:");
54
+	puts("");
55
+	puts("    -b addr     Local IP address to bind (Default: 0.0.0.0)");
56
+	puts("    -p port     Port to listen (Default: 8080)");
57
+	puts("    -d pidfile  Run in daemon mode and write PID file");
58
+	puts("");
59
+	puts("Stream settings:");
60
+	puts("");
61
+	puts("    -B          Disable stream burst for new clients (usefull for master servers)");
62
+	puts("");
63
+	usage_shown = 1;
64
+}
65
+
66
+static void config_parse(CONFIG *conf, int argc, char **argv) {
67
+	int j;
68
+	conf->channels_file = "config.channels";
69
+	conf->networks_file = "config.networks";
70
+	while ((j = getopt(argc, argv, "TRBhXmvb:c:C:d:i:l:L:n:p:s:x:V:H:P:F")) != -1) {
71
+		switch (j) {
72
+			case 'b':
73
+				conf->bind_to = optarg;
74
+				break;
75
+			case 'c':
76
+				conf->channels_file = optarg;
77
+				break;
78
+			case 'd':
79
+				conf->pidfile = optarg;
80
+				break;
81
+			case 'i':
82
+				conf->ident = optarg;
83
+				conf->logident = strdup(conf->ident);
84
+				char *c = conf->logident;
85
+				while (*c) {
86
+					if (*c=='/')
87
+						*c='-';
88
+					c++;
89
+				}
90
+				break;
91
+			case 'n':
92
+				conf->networks_file = optarg;
93
+				break;
94
+			case 'T':
95
+				conf->ts_sync = 1;
96
+				break;
97
+			case 'B':
98
+				conf->disable_burst = 1;
99
+				break;
100
+			case 'p':
101
+				conf->server_port = atoi(optarg);
102
+				break;
103
+			case 'F':
104
+				clean_on_exit = 1;
105
+				break;
106
+			case 'h':
107
+				show_usage();
108
+				exit(1);
109
+				break;
110
+		}
111
+	}
112
+	int is_error = 0;
113
+	if (!conf->ident) {
114
+		show_usage();
115
+		fprintf(stderr, "ERROR: Server ident is not set. Use '-i provider/streamer'\n");
116
+		is_error++;
117
+	}
118
+	if (!conf->channels_file) {
119
+		show_usage();
120
+		fprintf(stderr, "ERROR: Channel definitions file is not set, use '-c channels_file'\n");
121
+		is_error++;
122
+	}
123
+	if (!conf->networks_file) {
124
+		show_usage();
125
+		fprintf(stderr, "ERROR: Network definitions file is not set, use '-n networks_file'\n");
126
+		is_error++;
127
+	}
128
+	if (is_error)
129
+		exit(3);
130
+}
131
+
132
+static void init_channels(void) {
133
+	/* Init channels */
134
+	fputs("Reading channels map:\t\t",stderr);
135
+	int j = load_channels(config);
136
+	if (j != -1) {
137
+		fprintf(stderr,"[OK] %i channels read\n", j);
138
+	} else {
139
+		fprintf(stderr,"[ERR] Can't read channels.\n");
140
+		exit(1);
141
+	}
142
+}
143
+
144
+static void init_networks(void) {
145
+	/* Init networks */
146
+	fputs("Reading networks ACL:\t\t",stderr);
147
+	int j = load_networks(config);
148
+	if (j != -1) {
149
+		fprintf(stderr,"[OK] %i networks read\n", j);
150
+	} else {
151
+		fprintf(stderr,"[ERR] Can't read networks.\n");
152
+		exit(1);
153
+	}
154
+}
155
+
156
+static void handle_signal(int sig) {
157
+	if (!keep_running)
158
+		return;
159
+	rcvsig = sig;
160
+	keep_running = 0;
161
+	shutdown_fd(&config->server_socket);
162
+}
163
+
164
+int main(int argc, char **argv) {
165
+	set_http_response_server_ident(server_sig, server_ver);
166
+
167
+	printf("%s %s Git: %s\n", server_sig, server_ver, BUILD_ID);
168
+	printf("%s\n\n", COPYRIGHT);
169
+
170
+	restreamer = list_new("restreamer");
171
+	clients = list_new("clients");
172
+
173
+	memset(&allstats, 0, sizeof(allstats));
174
+	allstats.start_ts = time(NULL);
175
+	uname(&allstats.utsdata);
176
+
177
+	memset(&TS_NULL_FRAME, 0xff, FRAME_PACKET_SIZE);
178
+	int i;
179
+	for (i=0; i<FRAME_PACKET_SIZE; i=i+188) {
180
+		TS_NULL_FRAME[i+0] = 0x47;
181
+		TS_NULL_FRAME[i+1] = 0x1f;
182
+		TS_NULL_FRAME[i+2] = 0xff;
183
+		TS_NULL_FRAME[i+3] = 0x00;
184
+	}
185
+
186
+	regcomp(&request_get, "^GET /([^ ]*) HTTP/1.*$", REG_EXTENDED);
187
+	regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
188
+
189
+	config = config_alloc();
190
+	config_parse(config, argc, argv);
191
+
192
+	init_channels();
193
+	init_networks();
194
+	init_server_socket(config->bind_to, config->server_port, &config->server_name, &config->server_socket);
195
+	daemonize(config->pidfile);
196
+
197
+	/* Must be called after daemonize! */
198
+	log_init(config->logident, 0, config->pidfile == NULL, NULL, 0);
199
+
200
+	/* Catch some signals */
201
+	signal(SIGCHLD, SIG_IGN);
202
+	signal(SIGPIPE, SIG_IGN);
203
+	signal(SIGHUP, SIG_IGN);
204
+	signal(SIGINT, handle_signal);
205
+	signal(SIGTERM, handle_signal);
206
+
207
+	LOGf("START: %s %s (%s)\n" , server_sig, server_ver, config->ident);
208
+
209
+	while(keep_running) {
210
+		struct sockaddr_in client;
211
+		unsigned int clientlen = sizeof(client);
212
+		int clientsock;
213
+		clientsock = accept(config->server_socket, (struct sockaddr *) &client, &clientlen);
214
+		if (clientsock < 0) {
215
+			if (config->server_socket > -1 && errno != EINVAL)	// The server_socket is closed on exit, so do not report errors
216
+				LOGf("ERROR: Failed to accept client fd: %i err: %s\n", clientsock, strerror(errno));
217
+			if (errno==EMFILE || errno==ENFILE) /* No more FDs */
218
+				break;
219
+		} else {
220
+			request_info *req;
221
+			pthread_t req_thread;
222
+			req = malloc(sizeof(request_info));
223
+			if (!req) {
224
+				log_perror("Can't allocate request_info", errno);
225
+				continue;
226
+			}
227
+			req->clientsock = clientsock;
228
+			req->client = client;
229
+			if (pthread_create(&req_thread, NULL, (void *)&process_request, (void *)req)) {
230
+				log_perror("Error creating request processing thread.", errno);
231
+				exit(1);
232
+			}
233
+			pthread_detach(req_thread); 
234
+		}
235
+	}
236
+
237
+	restreamer_stop_all(restreamer);
238
+
239
+	LOGf("SHUTDOWN: Signal %i | %s %s (%s)\n", rcvsig, server_sig, server_ver, config->ident);
240
+
241
+	if (clean_on_exit) {
242
+		regfree(&http_response);
243
+		regfree(&request_get);
244
+		list_free(&restreamer, (void (*)(void *))free_restreamer, NULL);
245
+		list_free(&clients, (void (*)(void *))stop_client_shutdown, NULL);
246
+		list_free(&netconf, (void (*)(void *))free, NULL);
247
+		list_free(&chanconf, (void (*)(void *))free_channel, NULL);
248
+	}
249
+
250
+	log_close();
251
+
252
+	config_free(&config);
253
+
254
+	exit(0);
255
+}

Loading…
Cancel
Save