Browse Source

Add support for IPv6 and for multiple CAMD addresses.

This patch adds support for connecting to CAMD over IPv6. Nice addition
is that now tsdecrypt tries each of the addresses returned when CAMD
hostname is resolved and connects to the one that works. Resolving
of the server address is done before each connect, which allows for
uninterupted migration of CAMD servers by just changing their DNS
record.

IPv6 functionality can be disabled by using -4/--ipv4 options or
if you fancy only IPv6 servers -6/--ipv6 can be used.
Georgi Chorbadzhiyski 11 years ago
parent
commit
8f3321b270
13 changed files with 147 additions and 62 deletions
  1. 6
    0
      ChangeLog
  2. 8
    0
      README
  3. 1
    2
      TODO
  4. 1
    1
      camd-cs378x.c
  5. 1
    1
      camd-newcamd.c
  6. 45
    18
      camd.c
  7. 1
    1
      camd.h
  8. 1
    1
      data.c
  9. 2
    2
      data.h
  10. 14
    5
      tsdecrypt.1
  11. 47
    31
      tsdecrypt.c
  12. 18
    0
      util.c
  13. 2
    0
      util.h

+ 6
- 0
ChangeLog View File

@@ -7,6 +7,12 @@
7 7
    set by IP address.
8 8
  * Allow tsdecrypt to read ECM/EMM packet from text file. The added
9 9
    parameters are -n, -ecm-file <file> and -m --emm-file <file>.
10
+ * Add support for CAMD server listening on IPv6.
11
+ * Add support for forcing only IPv4 (-4, --ipv4) or IPv6 (-6, --ipv6)
12
+   server connections.
13
+ * Add support for multiple CAMD addresses returned when resolving CAMD
14
+   server hostname. tsdecrypt would try each of the addresses and use
15
+   the one that works.
10 16
 
11 17
 2012-04-19 : Version 8.1
12 18
  * Add support for Bulcrypt CAS.

+ 8
- 0
README View File

@@ -143,10 +143,18 @@ CAMD server options:
143 143
  -A --camd-proto <proto>    | Set CAMD network protocol.
144 144
                             . Valid protocols are: CS378X (default) and NEWCAMD
145 145
  -s --camd-server <host>    | Set CAMD server address. Default port: 2233
146
+                            . Example IPv4 addr and port: 127.0.0.1:2233
147
+                            . Example IPv6 addr and port: [2a00::1014]:2233
148
+                            . Example hostname          : example.com
149
+                            . Example hostname and port : example.com:2233
150
+                            . Example IPv4 hostname     : ipv4.google.com
151
+                            . Example IPv6 hostname     : ipv6.google.com
146 152
  -U --camd-user <user>      | Set CAMD server user. Default: user
147 153
  -P --camd-pass <pass>      | Set CAMD server password. Default: pass
148 154
  -B --camd-des-key <key>    | Set DES key for newcamd protocol.
149 155
                             . Default: 0102030405060708091011121314
156
+ -4 --ipv4                  | Use only IPv4 addresses of the camd server.
157
+ -6 --ipv6                  | Use only IPv6 addresses of the camd server.
150 158
 
151 159
 EMM options:
152 160
  -e --emm                   | Enable sending EMM's to CAMD. Default: disabled

+ 1
- 2
TODO View File

@@ -1,4 +1,3 @@
1
-- Resolve server address on each connect.
2 1
 - Sort nanos of viaccess and cryptoworks emms.
3
-- Add ipv6 support (multicast send/recv, camd connection).
2
+- Add ipv6 support (multicast send/recv).
4 3
 - Investigate EMM filters implemented in https://github.com/joolzg/tsdecrypt

+ 1
- 1
camd-cs378x.c View File

@@ -27,7 +27,7 @@
27 27
 
28 28
 static int cs378x_connect(struct camd *c) {
29 29
 	if (c->server_fd < 0)
30
-		c->server_fd = camd_tcp_connect(c->server_addr, c->server_port);
30
+		c->server_fd = connect_client(SOCK_STREAM, c->hostname, c->service);
31 31
 	return c->server_fd;
32 32
 }
33 33
 

+ 1
- 1
camd-newcamd.c View File

@@ -345,7 +345,7 @@ static int newcamd_login(struct camd *c) {
345 345
 
346 346
 static int newcamd_connect(struct camd *c) {
347 347
 	if (c->server_fd < 0) {
348
-		c->server_fd = camd_tcp_connect(c->server_addr, c->server_port);
348
+		c->server_fd = connect_client(SOCK_STREAM, c->hostname, c->service);
349 349
 		if (!newcamd_login(c)) {
350 350
 			shutdown_fd(&c->server_fd);
351 351
 			return -1;

+ 45
- 18
camd.c View File

@@ -29,33 +29,60 @@
29 29
 #include "camd.h"
30 30
 #include "notify.h"
31 31
 
32
+int ai_family = AF_UNSPEC;
33
+
32 34
 static uint8_t invalid_cw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
33 35
 
34
-int camd_tcp_connect(struct in_addr ip, int port) {
35
-	ts_LOGf("CAM | Connecting to server %s:%d\n", inet_ntoa(ip), port);
36
+int connect_client(int socktype, const char *hostname, const char *service) {
37
+	struct addrinfo hints, *res;
38
+	int n;
36 39
 
37
-	int fd = socket(PF_INET, SOCK_STREAM, 0);
38
-	if (fd < 0)	{
39
-		ts_LOGf("CAM | Could not create socket | %s\n", strerror(errno));
40
+	memset(&hints, 0, sizeof(struct addrinfo));
41
+
42
+	hints.ai_family = ai_family;
43
+	hints.ai_socktype = socktype;
44
+
45
+	ts_LOGf("CAM | Connecting to server %s port %s\n", hostname, service);
46
+
47
+	n = getaddrinfo(hostname, service, &hints, &res);
48
+
49
+	if (n < 0) {
50
+		ts_LOGf("CAM | ERROR: getaddrinfo(%s): %s\n", hostname, gai_strerror(n));
40 51
 		return -1;
41 52
 	}
42 53
 
43
-	struct sockaddr_in sock;
44
-	sock.sin_family = AF_INET;
45
-	sock.sin_port = htons(port);
46
-	sock.sin_addr = ip;
47
-	if (do_connect(fd, (struct sockaddr *)&sock, sizeof(sock), 1000) < 0) {
48
-		ts_LOGf("CAM | Could not connect to server %s:%d | %s\n", inet_ntoa(ip), port, strerror(errno));
49
-		close(fd);
50
-		sleep(1);
51
-		return -1;
54
+	int sockfd = -1;
55
+	struct addrinfo *ressave = res;
56
+	while (res) {
57
+		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
58
+		if (sockfd > -1) {
59
+			if (do_connect(sockfd, res->ai_addr, res->ai_addrlen, 1000) < 0) {
60
+				char str_addr[INET6_ADDRSTRLEN];
61
+				my_inet_ntop(res->ai_family, res->ai_addr, str_addr, sizeof(str_addr));
62
+				ts_LOGf("CAM | Could not connect to server %s:%s (%s) | %s\n",
63
+					hostname, service, str_addr, strerror(errno));
64
+				close(sockfd);
65
+				sockfd = -1;
66
+			} else {
67
+				break; // connected
68
+			}
69
+		} else {
70
+			ts_LOGf("CAM | Could not create socket: %s\n", strerror(errno));
71
+			sleep(1);
72
+			return -1;
73
+		}
74
+		res = res->ai_next;
75
+	}
76
+	freeaddrinfo(ressave);
77
+
78
+	if (socktype == SOCK_STREAM) {
79
+		int flag = 1;
80
+		setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
52 81
 	}
53 82
 
54
-	int flag = 1;
55
-	setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
83
+	ts_LOGf("CAM | Connected to fd:%d\n", sockfd);
56 84
 
57
-	ts_LOGf("CAM | Connected to fd:%d\n", fd);
58
-	return fd;
85
+	return sockfd;
59 86
 }
60 87
 
61 88
 static inline void camd_reconnect(struct camd *c) {

+ 1
- 1
camd.h View File

@@ -17,7 +17,7 @@
17 17
 
18 18
 #include "data.h"
19 19
 
20
-int						camd_tcp_connect	(struct in_addr ip, int port);
20
+int connect_client							(int socktype, const char *hostname, const char *service);
21 21
 
22 22
 struct camd_msg *		camd_msg_alloc		(enum msg_type msg_type, uint16_t ca_id, uint16_t service_id, uint8_t *data, uint8_t data_len);
23 23
 void					camd_msg_free   	(struct camd_msg **pmsg);

+ 1
- 1
data.c View File

@@ -56,7 +56,7 @@ void data_init(struct ts *ts) {
56 56
 	// CAMD
57 57
 	memset(&ts->camd, 0, sizeof(ts->camd));
58 58
 	ts->camd.server_fd    = -1;
59
-	ts->camd.server_port  = 2233;
59
+	ts->camd.service      = "2233";
60 60
 	ts->camd.key          = &ts->key;
61 61
 	ts->camd.user         = "user";
62 62
 	ts->camd.pass         = "pass";

+ 2
- 2
data.h View File

@@ -138,8 +138,8 @@ struct newcamd {
138 138
 
139 139
 struct camd {
140 140
 	int				server_fd;
141
-	struct in_addr	server_addr;
142
-	unsigned int	server_port;
141
+	char			*hostname;
142
+	char			*service;
143 143
 	char			*user;
144 144
 	char			*pass;
145 145
 

+ 14
- 5
tsdecrypt.1 View File

@@ -177,11 +177,12 @@ Set CAMD server protocol. Valid protocols are \fBCS378X\fR and \fBNEWCAMD\fR.
177 177
 If this option is not used the default protocol is \fBCS378X\fR (camd35 over
178 178
 tcp).
179 179
 .TP
180
-\fB\-s\fR, \fB\-\-camd\-server\fR <host[:port]>
181
-Set CAMD server address. You can use IP address or hostname. If the port is not
182
-set then \fB2233\fR is used as default port. 2233 is the default port for
183
-CS378X protocol but for NEWCAMD protocol you probably should choose other
184
-port number.
180
+\fB\-s\fR, \fB\-\-camd\-server\fR <hostname[:port]>
181
+Set CAMD server address. You can use IPv4/IPv6 address or hostname. If the port
182
+is not set then \fB2233\fR is used as default port. 2233 is the default port
183
+for CS378X protocol but for NEWCAMD protocol you probably should choose other
184
+port number. To set static IPv6 address you have to put in in brackets (\fB[]\fR)
185
+for example: \fB[1234::5678]:2233\fR
185 186
 .TP
186 187
 \fB\-U\fR, \fB\-\-camd\-user\fR <username>
187 188
 Set CAMD user name. The default is \fBuser\fR.
@@ -193,6 +194,14 @@ Set CAMD user password. The default is \fBpass\fR.
193 194
 Set DES key used by NEWCAMD protocol. The default
194 195
 is \fB0102030405060708091011121314\fR.
195 196
 .TP
197
+\fB\-4\fR, \fB\-\-ipv4\fR
198
+Connect to CAMD server using only IPv4 addresses of the server. IPv6
199
+addresses would be are ignorred.
200
+.TP
201
+\fB\-6\fR, \fB\-\-ipv6\fR
202
+Connect to CAMD server using only IPv6 addresses of the server. IPv4
203
+addresses would be are ignorred.
204
+.TP
196 205
 .SH EMM OPTIONS
197 206
 .PP
198 207
 .TP

+ 47
- 31
tsdecrypt.c View File

@@ -52,6 +52,8 @@ static int packet_buflen;
52 52
 static uint8_t packet_buf[256];
53 53
 static enum msg_type packet_type = ECM_MSG;
54 54
 
55
+extern int ai_family;
56
+
55 57
 static void do_log(FILE *f, time_t now, const char *msg) {
56 58
 	char date[64];
57 59
 	struct tm tm;
@@ -76,9 +78,9 @@ static void LOG_func(const char *msg) {
76 78
 		LOG(msg);
77 79
 }
78 80
 
79
-static const char short_options[] = "i:d:N:Sl:L:F:I:RzM:T:W:O:o:t:rk:g:upwxyc:C:Y:Q:A:s:U:P:B:eZ:Ef:X:H:G:KJ:D:jbhVn:m:";
81
+static const char short_options[] = "i:d:N:Sl:L:F:I:RzM:T:W:O:o:t:rk:g:upwxyc:C:Y:Q:A:s:U:P:B:46eZ:Ef:X:H:G:KJ:D:jbhVn:m:";
80 82
 
81
-// Unused short options: aqv0123456789
83
+// Unused short options: aqv01235789
82 84
 static const struct option long_options[] = {
83 85
 	{ "ident",				required_argument, NULL, 'i' },
84 86
 	{ "daemon",				required_argument, NULL, 'd' },
@@ -117,6 +119,8 @@ static const struct option long_options[] = {
117 119
 	{ "camd-user",			required_argument, NULL, 'U' },
118 120
 	{ "camd-pass",			required_argument, NULL, 'P' },
119 121
 	{ "camd-des-key",		required_argument, NULL, 'B' },
122
+	{ "ipv4",				no_argument,       NULL, '4' },
123
+	{ "ipv6",				no_argument,       NULL, '6' },
120 124
 
121 125
 	{ "emm",				no_argument,       NULL, 'e' },
122 126
 	{ "emm-pid",			required_argument, NULL, 'Z' },
@@ -194,10 +198,18 @@ static void show_help(struct ts *ts) {
194 198
 	printf(" -A --camd-proto <proto>    | Set CAMD network protocol.\n");
195 199
 	printf("                            . Valid protocols are: CS378X (default) and NEWCAMD\n");
196 200
 	printf(" -s --camd-server <host>    | Set CAMD server address. Default port: 2233\n");
201
+	printf("                            . Example IPv4 addr and port: 127.0.0.1:2233\n");
202
+	printf("                            . Example IPv6 addr and port: [2a00::1014]:2233\n");
203
+	printf("                            . Example hostname          : example.com\n");
204
+	printf("                            . Example hostname and port : example.com:2233\n");
205
+	printf("                            . Example IPv4 hostname     : ipv4.google.com\n");
206
+	printf("                            . Example IPv6 hostname     : ipv6.google.com\n");
197 207
 	printf(" -U --camd-user <user>      | Set CAMD server user. Default: %s\n", ts->camd.user);
198 208
 	printf(" -P --camd-pass <pass>      | Set CAMD server password. Default: %s\n", ts->camd.pass);
199 209
 	printf(" -B --camd-des-key <key>    | Set DES key for newcamd protocol.\n");
200 210
 	printf("                            . Default: %s\n", ts->camd.newcamd.hex_des_key);
211
+	printf(" -4 --ipv4                  | Use only IPv4 addresses of the camd server.\n");
212
+	printf(" -6 --ipv6                  | Use only IPv6 addresses of the camd server.\n");
201 213
 	printf("\n");
202 214
 	printf("EMM options:\n");
203 215
 	printf(" -e --emm                   | Enable sending EMM's to CAMD. Default: %s\n", ts->emm_send ? "enabled" : "disabled");
@@ -267,8 +279,6 @@ static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_
267 279
 }
268 280
 
269 281
 static void parse_options(struct ts *ts, int argc, char **argv) {
270
-	int h_err;
271
-	struct addrinfo hints, *addrinfo = NULL;
272 282
 	int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0, port_set = 0;
273 283
 	while ((j = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
274 284
 		char *p = NULL;
@@ -438,33 +448,29 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
438 448
 				}
439 449
 				break;
440 450
 			case 's': // --camd-server
441
-				p = strrchr(optarg, ':');
442
-				if (p) {
443
-					*p = 0x00;
444
-					ts->camd.server_port = atoi(p + 1);
445
-					port_set = 1;
446
-				}
447
-				server_err = 1;
448
-
449
-				memset(&hints, 0, sizeof hints);
450
-				hints.ai_family = AF_INET;
451
-				hints.ai_socktype = SOCK_STREAM;
452
-				h_err = getaddrinfo(optarg, NULL, &hints, &addrinfo);
453
-				if (h_err == 0) {
454
-					int num_addrs = 0;
455
-					struct addrinfo *addr;
456
-					for (addr = addrinfo; addr != NULL; addr = addr->ai_next) {
457
-						struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr->ai_addr;
458
-						if (!ts->camd.server_addr.s_addr) // Get the first IP address
459
-							ts->camd.server_addr = ipv4->sin_addr;
460
-						num_addrs++;
451
+				server_err = 0;
452
+				ts->camd.hostname = optarg;
453
+				if (optarg[0] == '[') { // Detect IPv6 static address
454
+					p = strrchr(optarg, ']');
455
+					if (!p) {
456
+						fprintf(stderr, "ERROR: Invalid IPv6 address format: %s\n", optarg);
457
+						exit(EXIT_FAILURE);
458
+					}
459
+					ts->camd.hostname = optarg + 1; // Remove first [
460
+					*p = 0x00; // Remove last ]
461
+					char *p2 = strchr(p + 1, ':');
462
+					if (p2) {
463
+						*p2 = 0x00;
464
+						ts->camd.service = p2 + 1;
465
+						port_set = 1;
461 466
 					}
462
-					freeaddrinfo(addrinfo);
463
-					if (num_addrs)
464
-						server_err = 0;
465 467
 				} else {
466
-					fprintf(stderr, "ERROR: getaddrinfo(%s) returned: %s\n", optarg, gai_strerror(h_err));
467
-					exit(EXIT_FAILURE);
468
+					p = strrchr(optarg, ':');
469
+					if (p) {
470
+						*p = 0x00;
471
+						ts->camd.service = p + 1;
472
+						port_set = 1;
473
+					}
468 474
 				}
469 475
 				break;
470 476
 			case 'U': // --camd-user
@@ -484,6 +490,12 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
484 490
 				strncpy(ts->camd.newcamd.hex_des_key, optarg, sizeof(ts->camd.newcamd.hex_des_key) - 1);
485 491
 				ts->camd.newcamd.hex_des_key[sizeof(ts->camd.newcamd.hex_des_key) - 1] = 0;
486 492
 				break;
493
+			case '4': // --ipv4
494
+				ai_family = AF_INET;
495
+				break;
496
+			case '6': // --ipv6
497
+				ai_family = AF_INET6;
498
+				break;
487 499
 
488 500
 			case 'e': // --emm
489 501
 				ts->emm_send = !ts->emm_send;
@@ -621,7 +633,7 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
621 633
 		exit(EXIT_FAILURE);
622 634
 	}
623 635
 	if (ts->camd.ops.proto == CAMD_NEWCAMD && !port_set) {
624
-		fprintf(stderr, "ERROR: CAMD server port is not set. Use --camd-server %s:xxxx to set the port.\n", inet_ntoa(ts->camd.server_addr));
636
+		fprintf(stderr, "ERROR: CAMD server port is not set. Use --camd-server %s:xxxx to set the port.\n", ts->camd.hostname);
625 637
 		exit(EXIT_FAILURE);
626 638
 	}
627 639
 
@@ -728,7 +740,11 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
728 740
 	}
729 741
 	if (!ts->camd.constant_codeword) {
730 742
 		ts_LOGf("CAMD proto : %s\n", ts->camd.ops.ident);
731
-		ts_LOGf("CAMD addr  : tcp://%s:%u/\n", inet_ntoa(ts->camd.server_addr), ts->camd.server_port);
743
+		ts_LOGf("CAMD addr  : %s:%s%s\n", ts->camd.hostname, ts->camd.service,
744
+			ai_family == AF_INET  ? " (IPv4 only)" :
745
+			ai_family == AF_INET6 ? " (IPv6 only)" :
746
+			" (IPv4/IPv6)"
747
+		);
732 748
 		ts_LOGf("CAMD user  : %s\n", ts->camd.user);
733 749
 		ts_LOGf("CAMD pass  : %s\n", ts->camd.pass);
734 750
 		if (ts->camd.ops.proto == CAMD_NEWCAMD)

+ 18
- 0
util.c View File

@@ -20,6 +20,7 @@
20 20
 #include <time.h>
21 21
 #include <string.h>
22 22
 #include <errno.h>
23
+#include <arpa/inet.h>
23 24
 
24 25
 #include "util.h"
25 26
 
@@ -221,3 +222,20 @@ OUT:
221 222
 
222 223
 	return buf_pos;
223 224
 }
225
+
226
+char *my_inet_ntop(int family, struct sockaddr *addr, char *dest, int dest_len) {
227
+	struct sockaddr_in  *addr_v4 = (struct sockaddr_in  *)addr;
228
+	struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)addr;
229
+	switch (family) {
230
+		case AF_INET:
231
+			return (char *)inet_ntop(AF_INET, &addr_v4->sin_addr, dest, dest_len);
232
+			break;
233
+		case AF_INET6:
234
+			return (char *)inet_ntop(AF_INET6, &addr_v6->sin6_addr, dest, dest_len);
235
+			break;
236
+		default:
237
+			memset(dest, 0, dest_len);
238
+			strcpy(dest, "unknown");
239
+			return dest;
240
+	}
241
+}

+ 2
- 0
util.h View File

@@ -16,6 +16,7 @@
16 16
 #define UTIL_H
17 17
 
18 18
 #include <inttypes.h>
19
+#include <arpa/inet.h>
19 20
 
20 21
 unsigned long crc32(unsigned long crc, const uint8_t *buf, unsigned int len);
21 22
 int32_t boundary(int32_t exp, int32_t n);
@@ -26,5 +27,6 @@ void set_thread_name(char *thread_name);
26 27
 int decode_hex_string(char *hex, uint8_t *bin, int asc_len);
27 28
 int64_t get_time(void);
28 29
 unsigned int file_hex2buf(char *filename, uint8_t *buffer, unsigned int buf_size);
30
+char *my_inet_ntop(int family, struct sockaddr *addr, char *dest, int dest_len);
29 31
 
30 32
 #endif

Loading…
Cancel
Save