-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcast
More file actions
executable file
·158 lines (131 loc) · 4.37 KB
/
cast
File metadata and controls
executable file
·158 lines (131 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
"""
icmp-cast v1.3
Unified multicast/broadcast file sender.
Supports sending files via UDP to:
- Multicast (224.0.0.0 – 239.255.255.255)
- Broadcast (255.255.255.255 or subnet-specific)
"""
import sys
import os
import socket
import struct
import re
import glob
import time
def usage():
print("""
Usage:
icmp-cast <host:port> <file1> [file2 ... fileN] [--delay=SECONDS]
Arguments:
<host:port> Destination host/IP and UDP port.
Host must be a multicast (224.0.0.0–239.255.255.255)
or broadcast address (e.g., 255.255.255.255).
<file> One or more files to send. Wildcards like *.xml are supported.
--delay=SECONDS Optional delay (in seconds) between UDP packets.
Defaults to 0.3 seconds.
Useful if the receiver rate-limits fast senders.
Examples:
icmp-cast 239.2.3.1:6969 myfile.xml
icmp-cast 255.255.255.255:6969 *.xml --delay=0.1
icmp-cast myhost.local:6969 event1.xml event2.xml --delay=0.5
Wildcard Notes:
- Wildcards (e.g., `*.xml`) are expanded by your shell before the script runs.
- To handle large batches or avoid "argument list too long" errors, use a loop:
for f in *.xml; do
icmp-cast 239.2.3.1:6969 "$f"
done
- Quoting wildcards (e.g., "*.xml") disables expansion and will likely cause errors
unless handled within the script.
""")
sys.exit(1)
def is_multicast(ip):
try:
first_octet = int(ip.split('.')[0])
return 224 <= first_octet <= 239
except ValueError:
return False
def is_broadcast(ip):
return ip == '255.255.255.255'
def parse_target(target):
pattern = r'^([a-zA-Z0-9.\-]+):(\d+)$'
match = re.match(pattern, target)
if not match:
print(f"Error: Invalid target format: {target}")
usage()
host = match.group(1)
port = int(match.group(2))
if not 1 <= port <= 65535:
print(f"Error: Invalid port number: {port}")
usage()
return host, port
def send_file(file_path, sock, ip, port, mode):
try:
with open(file_path, 'rb') as f:
while True:
chunk = f.read(1024)
if not chunk:
break
sock.sendto(chunk, (ip, port))
print(f"{mode.capitalize()} complete: {file_path} → {ip}:{port}")
except Exception as e:
print(f"Error sending {file_path}: {e}")
def main():
if len(sys.argv) < 3:
usage()
# Extract optional --delay argument
delay = 0.3 #default value
args = sys.argv[1:]
delay_args = [arg for arg in args if arg.startswith('--delay=')]
for darg in delay_args:
try:
delay = float(darg.split('=')[1])
except ValueError:
print(f"Error: Invalid delay value in {darg}")
sys.exit(1)
# Remove --delay from file args
args = [a for a in args if not a.startswith('--delay=')]
if len(args) < 2:
usage()
target = args[0]
raw_file_args = args[1:]
ip, port = parse_target(target)
# Expand wildcards and collect file paths
files = []
for arg in raw_file_args:
expanded = glob.glob(arg)
if not expanded:
print(f"Warning: No match for {arg}")
files.extend(expanded)
if not files:
print("Error: No valid files to send.")
sys.exit(1)
# Determine mode
if is_multicast(ip):
mode = 'multicast'
elif is_broadcast(ip):
mode = 'broadcast'
else:
print(f"Error: IP address {ip} is neither multicast nor broadcast.")
sys.exit(1)
# Set up socket
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
if mode == 'multicast':
ttl = struct.pack('b', 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
elif mode == 'broadcast':
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
for file_path in files:
if os.path.isfile(file_path):
send_file(file_path, sock, ip, port, mode)
time.sleep(delay) # <-- Delay between each file
else:
print(f"Skipping non-file: {file_path}")
except Exception as e:
print(f"Socket setup failed: {e}")
sys.exit(1)
finally:
sock.close()
if __name__ == "__main__":
main()