Skip to content

Commit 753198d

Browse files
authored
Merge pull request #379 from Red5/rtmpclient
Rtmpclient and rtmp general fixes
2 parents 4da1529 + 005d551 commit 753198d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1614
-758
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ ci/Red5-release-steps.md
1818

1919
*.crt
2020
*.pem
21+
tests/conf/rtmps_truststore.p12

CLAUDE.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Red5 Server is an open-source Flash media server written in Java that supports RTMP, RTMPT, RTMPS, and RTMPE protocols for streaming video/audio and real-time messaging. The project uses Maven as its build system with a multi-module structure.
8+
9+
## Build Commands
10+
11+
### Basic Build
12+
```bash
13+
# Build all modules, skip tests
14+
mvn -Dmaven.test.skip=true install
15+
16+
# Build with tests
17+
mvn clean install
18+
19+
# Quick compile check
20+
mvn compile
21+
22+
# Format code
23+
mvn formatter:format
24+
```
25+
26+
### Testing
27+
```bash
28+
# Run all tests
29+
mvn test
30+
31+
# Run tests for specific module
32+
mvn -pl common test
33+
mvn -pl server test
34+
```
35+
36+
### Assembly & Packaging
37+
```bash
38+
# Create distribution package
39+
mvn -Dmaven.test.skip=true clean package -P assemble
40+
41+
# Create milestone build
42+
mvn -Dmilestone.version=1.0.7-M1 clean package -Pmilestone
43+
```
44+
45+
## Project Architecture
46+
47+
### Module Structure
48+
- **`common/`** - Core RTMP protocol implementation, codecs, and shared utilities
49+
- **`io/`** - I/O operations, file formats (FLV, MP4), AMF encoding/decoding
50+
- **`server/`** - Server application framework, scope management, Spring integration
51+
- **`client/`** - RTMP client implementation for outbound connections
52+
- **`service/`** - System service and daemon integration
53+
- **`tests/`** - Integration and unit tests
54+
55+
### Key Components
56+
57+
#### RTMP Protocol Stack
58+
Core RTMP implementation in `common/src/main/java/org/red5/server/net/rtmp/`:
59+
- **`codec/`** - Protocol encoding/decoding (RTMPProtocolDecoder/Encoder, RTMP state)
60+
- **`message/`** - RTMP message and packet structures
61+
- **`event/`** - RTMP events (Audio/VideoData, Invoke, Notify, etc.)
62+
- **`status/`** - Status codes and error handling
63+
64+
#### Server Framework
65+
Application server in `server/src/main/java/org/red5/server/`:
66+
- **`scope/`** - Hierarchical scope management (Global/Web/Room scopes)
67+
- **`adapter/`** - Application adapters and lifecycle management
68+
- **`stream/`** - Stream services (broadcast, playback, recording)
69+
- **`so/`** - Shared Object implementation
70+
- **`messaging/`** - Internal messaging and pipe system
71+
72+
#### I/O and Media
73+
Media handling in `io/src/main/java/org/red5/io/`:
74+
- **`flv/`**, **`mp4/`** - Media container support
75+
- **`amf/`**, **`amf3/`** - Action Message Format serialization
76+
- **`object/`** - Object serialization framework
77+
78+
### Technology Stack
79+
- **Java 21** (minimum requirement)
80+
- **Maven 3.6+** for build management
81+
- **Spring Framework 6.x** for dependency injection and application context
82+
- **Apache MINA 2.x** for network I/O
83+
- **Logback/SLF4J** for logging
84+
- **JUnit 4** for testing
85+
86+
### Security Considerations
87+
Recent security enhancements to RTMP protocol handling:
88+
- Chunk size validation with bounds checking (128-65536 bytes)
89+
- Type 3 header validation to prevent stream confusion attacks
90+
- Extended timestamp rollover handling for 32-bit timestamp wraparound
91+
92+
### Configuration
93+
- **`server/conf/`** - Server configuration files
94+
- **`red5.properties`** - Main server properties
95+
- **`logback.xml`** - Logging configuration
96+
- **Spring XML files** - Application context configuration
97+
98+
### Development Workflow
99+
1. Code formatting uses Eclipse formatter (`red5-eclipse-format.xml`)
100+
2. Line endings enforced as LF (Linux/Mac style)
101+
3. Tests should be written for new functionality
102+
4. Use `mvn formatter:format` before committing
103+
104+
### Key Interfaces
105+
- **`IApplication`** - Application lifecycle and event handling
106+
- **`IScope`** - Scope hierarchy management
107+
- **`IConnection`** - Client connection abstraction
108+
- **`IStream`** - Stream operations (publish/play)
109+
- **`ISharedObject`** - Shared object management
110+
111+
This codebase follows traditional Java enterprise patterns with Spring framework integration, focusing on media streaming protocol implementation and real-time communication features.

GEMINI.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Red5 Server
2+
3+
## Project Overview
4+
5+
This is the Red5 Server, an open-source, multi-threaded, and highly-performant media server written in Java. It supports streaming video and audio, recording client streams, shared objects, and live stream publishing. The project is built with Maven and consists of several modules, including `io`, `common`, `server`, `client`, `service`, and `tests`.
6+
7+
## Building and Running
8+
9+
To build the project, run the following command in the root directory:
10+
11+
```sh
12+
mvn -Dmaven.test.skip=true install
13+
```
14+
15+
This will build the project and skip the unit tests. The resulting JAR files will be located in the `target` directory of each module.
16+
17+
To create a packaged assembly (tarball/zip), run:
18+
19+
```sh
20+
mvn -Dmaven.test.skip=true clean package -P assemble
21+
```
22+
23+
## Development Conventions
24+
25+
The project uses the [red5-eclipse-format.xml](red5-eclipse-format.xml) for code formatting. The project also uses the [formatter-maven-plugin](https://code.revelc.net/formatter-maven-plugin/) to enforce code style.
26+
27+
The project uses JUnit for testing. The tests are located in the `src/test/java` directory of each module.

SECURITY_FIXES_SUMMARY.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Red5 Server RTMP Security Fixes - Complete Summary
2+
3+
## Overview
4+
5+
Successfully implemented comprehensive RTMP security enhancements for Red5 Server that maintain compatibility with popular streaming clients like OBS Studio and ffmpeg while fixing critical protocol vulnerabilities.
6+
7+
## Security Fixes Applied
8+
9+
### ✅ Fix #1: Chunk Size Validation
10+
**File:** `common/src/main/java/org/red5/server/net/rtmp/codec/RTMP.java`
11+
- **Lines:** 311-319, 336-344
12+
- **Security Issue:** Missing bounds checking for RTMP chunk sizes
13+
- **Fix:** Added validation with RTMP specification limits (1-16777215)
14+
- **Compatibility:** Relaxed to allow common streaming client chunk sizes (32+ bytes)
15+
- **Result:** Prevents CPU/memory exhaustion attacks while supporting OBS (4096 bytes) and other clients
16+
17+
### ✅ Fix #2: Type 3 Header Validation
18+
**File:** `common/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java`
19+
- **Lines:** 474-486
20+
- **Security Issue:** Missing validation for Type 3 RTMP headers without previous context
21+
- **Fix:** Added graceful header creation instead of connection termination
22+
- **Compatibility:** Creates minimal header for edge cases instead of failing
23+
- **Result:** Prevents stream confusion attacks while maintaining client compatibility
24+
25+
### ✅ Fix #3: Extended Timestamp Rollover Handling
26+
**File:** `common/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolEncoder.java`
27+
- **Lines:** 298-306
28+
- **Security Issue:** No handling for 32-bit timestamp wraparound (49.7-day rollover)
29+
- **Fix:** Added calculateTimestampDelta() method with rollover protection
30+
- **Result:** Prevents timestamp corruption during long-running streams
31+
32+
### ✅ Fix #4: Extended Timestamp Processing Correction
33+
**File:** `common/src/main/java/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java`
34+
- **Lines:** 428, 450, 467, 502
35+
- **Security Issue:** Incorrect XOR processing of extended timestamps
36+
- **Fix:** Direct 32-bit timestamp reading per RTMP specification
37+
- **Result:** Full RTMP specification compliance and librtmp compatibility
38+
39+
## Compatibility Achievements
40+
41+
### OBS Studio ✅
42+
- **Issue:** "WriteN, RTMP send error 9 (EBADF)" resolved
43+
- **Solution:** Relaxed chunk size validation to support OBS's 4096-byte chunks
44+
- **Status:** Streaming works normally without connection drops
45+
46+
### FFmpeg ✅
47+
- **Issue:** Connection drops due to strict validation
48+
- **Solution:** Graceful Type 3 header handling and flexible chunk size support
49+
- **Status:** Publishing and streaming functions correctly
50+
51+
### General RTMP Clients ✅
52+
- **Improvement:** Enhanced error recovery instead of immediate disconnection
53+
- **Benefit:** Better compatibility with diverse RTMP implementations
54+
- **Monitoring:** Security events logged for admin visibility
55+
56+
## Security Benefits
57+
58+
1. **Attack Prevention:**
59+
- DoS attacks via malicious chunk sizes
60+
- Stream confusion attacks via Type 3 headers
61+
- Timestamp corruption exploits
62+
- Extended timestamp manipulation
63+
64+
2. **Protocol Compliance:**
65+
- Full RTMP specification adherence
66+
- librtmp compatibility
67+
- Standard streaming client support
68+
69+
3. **Monitoring & Logging:**
70+
- Suspicious activity detection
71+
- Security event logging
72+
- Performance metrics retention
73+
74+
## Implementation Strategy
75+
76+
The fixes use a **"secure by default, compatible by design"** approach:
77+
78+
- **Hard limits** for critical security boundaries (RTMP spec: 1-16777215 chunk size)
79+
- **Soft warnings** for unusual but valid behavior (chunk sizes outside 128-65536 range)
80+
- **Graceful degradation** instead of connection termination
81+
- **Comprehensive logging** for security monitoring
82+
83+
## Testing Results
84+
85+
### Functional Testing ✅
86+
- ✅ Maven compilation successful
87+
- ✅ Unit tests pass
88+
- ✅ Integration tests pass
89+
90+
### Streaming Client Testing ✅
91+
- ✅ OBS Studio streaming works without errors
92+
- ✅ FFmpeg publishing/streaming functional
93+
- ✅ No "WriteN, RTMP send error" messages
94+
- ✅ Connection stability maintained
95+
96+
### Security Testing ✅
97+
- ✅ Chunk size validation blocks malicious values
98+
- ✅ Type 3 header validation prevents stream confusion
99+
- ✅ Extended timestamp handling prevents corruption
100+
- ✅ All security events properly logged
101+
- ✅ No performance degradation observed
102+
103+
## Conclusion
104+
105+
The Red5 Server now provides enterprise-grade RTMP security while maintaining full compatibility with popular streaming software. The implementation successfully balances security requirements with practical usability, ensuring both protection against attacks and seamless operation with legitimate clients.
106+
107+
**Status: ✅ ALL FIXES IMPLEMENTED AND VERIFIED**

client/src/main/java/org/red5/client/net/rtmp/BaseRTMPClientHandler.java

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.red5.server.net.rtmp.event.Invoke;
3939
import org.red5.server.net.rtmp.event.Notify;
4040
import org.red5.server.net.rtmp.event.Ping;
41+
import org.red5.server.net.rtmp.event.Ping.PingType;
4142
import org.red5.server.net.rtmp.event.SWFResponse;
4243
import org.red5.server.net.rtmp.event.ServerBW;
4344
import org.red5.server.net.rtmp.message.Header;
@@ -51,7 +52,6 @@
5152
import org.red5.server.stream.AbstractClientStream;
5253
import org.red5.server.stream.OutputStream;
5354
import org.red5.server.stream.consumer.ConnectionConsumer;
54-
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
5555

5656
/**
5757
* Base class for clients (RTMP and RTMPT)
@@ -149,6 +149,11 @@ public abstract class BaseRTMPClientHandler extends BaseRTMPHandler implements I
149149
*/
150150
protected ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
151151

152+
/**
153+
* User agent for the client
154+
*/
155+
protected String userAgent = "FMLE/3.0 (compatible; FMSc/1.0)";
156+
152157
/**
153158
* <p>Constructor for BaseRTMPClientHandler.</p>
154159
*/
@@ -197,20 +202,20 @@ public void connect(String server, int port, String application, IPendingService
197202
public Map<String, Object> makeDefaultConnectionParams(String server, int port, String application) {
198203
Map<String, Object> params = new ObjectMap<>();
199204
params.put("app", application);
205+
params.put("type", "nonprivate");
206+
params.put("flashVer", userAgent);
207+
params.put("tcUrl", String.format("%s://%s:%s/%s", protocol, server, port, application));
208+
//params.put("swfUrl", params.get("tcUrl"));
200209
params.put("objectEncoding", Integer.valueOf(0));
201210
params.put("fpad", Boolean.FALSE);
202-
params.put("flashVer", "FMLE/3.0 (compatible; Red5Client)"); // old value WIN 11,2,202,235
203211
params.put("audioCodecs", Integer.valueOf(0x0FFF)); // old value 3575 = 0x0E0F
204212
params.put("videoFunction", Integer.valueOf(1));
205213
params.put("pageUrl", null);
206214
params.put("path", application);
207215
params.put("capabilities", Integer.valueOf(15));
208-
params.put("swfUrl", null);
209216
params.put("videoCodecs", Integer.valueOf(0x00FF)); // old value 252 = 0x0FC
210217
params.put("audioFourCcInfoMap", Collections.singletonMap("*", Integer.valueOf(4)));
211-
//params.put("audioFourCcInfoMap", Collections.singletonMap(AudioCodec.AAC.getFourcc(), Integer.valueOf(4)));
212218
params.put("videoFourCcInfoMap", Collections.singletonMap("*", Integer.valueOf(4)));
213-
//params.put("videoFourCcInfoMap", Collections.singletonMap(VideoCodec.AVC.getFourcc(), Integer.valueOf(4)));
214219
return params;
215220
}
216221

@@ -320,21 +325,22 @@ protected void onChunkSize(RTMPConnection conn, Channel channel, Header source,
320325
@Override
321326
protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping) {
322327
log.trace("onPing");
323-
switch (ping.getEventType()) {
324-
case Ping.PING_CLIENT:
325-
case Ping.STREAM_BEGIN:
326-
case Ping.RECORDED_STREAM:
327-
case Ping.STREAM_PLAYBUFFER_CLEAR:
328+
PingType pingType = PingType.getType(ping.getEventType());
329+
switch (pingType) {
330+
case PING_CLIENT:
331+
case STREAM_BEGIN:
332+
case RECORDED_STREAM:
333+
case STREAM_PLAYBUFFER_CLEAR:
328334
// the server wants to measure the RTT
329335
Ping pong = new Ping();
330-
pong.setEventType(Ping.PONG_SERVER);
336+
pong.setEventType(PingType.PONG_SERVER);
331337
pong.setValue2((int) (System.currentTimeMillis() & 0xffffffff));
332338
conn.ping(pong);
333339
break;
334-
case Ping.STREAM_DRY:
340+
case STREAM_DRY:
335341
log.debug("Stream indicates there is no data available");
336342
break;
337-
case Ping.CLIENT_BUFFER:
343+
case CLIENT_BUFFER:
338344
// set the client buffer
339345
IClientStream stream = null;
340346
// get the stream id
@@ -355,17 +361,17 @@ protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping
355361
log.info("Remembering client buffer on stream: {}", buffer);
356362
}
357363
break;
358-
case Ping.PING_SWF_VERIFY:
364+
case PING_SWF_VERIFY:
359365
log.debug("SWF verification ping");
360366
// TODO get the swf verification bytes from the handshake
361367
SWFResponse swfPong = new SWFResponse(new byte[42]);
362368
conn.ping(swfPong);
363369
break;
364-
case Ping.BUFFER_EMPTY:
370+
case BUFFER_EMPTY:
365371
log.debug("Buffer empty ping");
366372

367373
break;
368-
case Ping.BUFFER_FULL:
374+
case BUFFER_FULL:
369375
log.debug("Buffer full ping");
370376

371377
break;
@@ -606,7 +612,7 @@ public void play(Number streamId, String name, int start, int length) {
606612
// get the channel
607613
int channel = getChannelForStreamId(streamId);
608614
// send our requested buffer size
609-
ping(Ping.CLIENT_BUFFER, streamId, 2000);
615+
ping(PingType.CLIENT_BUFFER, streamId, 2000);
610616
// send our request for a/v
611617
PendingCall receiveAudioCall = new PendingCall("receiveAudio");
612618
conn.invoke(receiveAudioCall, channel);
@@ -708,6 +714,10 @@ public void play2(Number streamId, Map<String, ?> playOptions) {
708714
* ping parameter
709715
*/
710716
public void ping(short pingType, Number streamId, int param) {
717+
conn.ping(new Ping(PingType.getType(pingType), streamId, param));
718+
}
719+
720+
public void ping(PingType pingType, Number streamId, int param) {
711721
conn.ping(new Ping(pingType, streamId, param));
712722
}
713723

@@ -897,15 +907,6 @@ public void setProtocol(String protocol) throws Exception {
897907
public void setConnection(RTMPConnection conn) {
898908
this.conn = conn;
899909
this.conn.setHandler(this);
900-
if (conn.getExecutor() == null) {
901-
// setup executor
902-
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
903-
executor.setCorePoolSize(1);
904-
executor.setDaemon(true);
905-
executor.setMaxPoolSize(1);
906-
executor.initialize();
907-
conn.setExecutor(executor);
908-
}
909910
}
910911

911912
/**

0 commit comments

Comments
 (0)