Skip to content

Commit 179b8dd

Browse files
committed
Enable SOCKS for all & raw tunnels behind a flag
1 parent 2aae2a2 commit 179b8dd

File tree

12 files changed

+254
-14
lines changed

12 files changed

+254
-14
lines changed

src/components/view/http/http-details-pane.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ export class HttpDetailsPane extends React.Component<{
462462
// reset when we switch between exchanges:
463463
key={`${this.cardProps.webSocketMessages.key}-${this.props.exchange.id}`}
464464
streamId={this.props.exchange.id}
465-
streamType='WebSocket'
465+
cardHeading='WebSocket Messages'
466466

467467
editorNode={this.props.streamMessageEditor}
468468

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import * as _ from 'lodash';
2+
import * as React from 'react';
3+
import * as portals from 'react-reverse-portal';
4+
5+
import { getReadableIP } from '../../model/network';
6+
import { RawTunnel } from '../../model/raw-tunnel';
7+
8+
import { MediumCard } from '../common/card';
9+
import { ContentLabelBlock, Content, CopyableMonoValue } from '../common/text-content';
10+
import { PaneScrollContainer } from './view-details-pane';
11+
import { StreamMessageListCard } from './stream-message-list-card';
12+
import { SelfSizedEditor } from '../editor/base-editor';
13+
14+
export class RawTunnelDetailsPane extends React.Component<{
15+
tunnel: RawTunnel,
16+
streamMessageEditor: portals.HtmlPortalNode<typeof SelfSizedEditor>,
17+
isPaidUser: boolean
18+
}> {
19+
render() {
20+
const { tunnel } = this.props;
21+
22+
const sourceDetailParts = getReadableIP(tunnel.remoteIpAddress).split(' ');
23+
const sourceIp = sourceDetailParts[0];
24+
const sourceDetails = sourceDetailParts.slice(1).join(' ');
25+
26+
return <PaneScrollContainer>
27+
<MediumCard>
28+
<header>
29+
<h1>Raw Tunnel</h1>
30+
</header>
31+
32+
<ContentLabelBlock>Details</ContentLabelBlock>
33+
<Content>
34+
<p>
35+
This connection was not intercepted by HTTP Toolkit, as it contains
36+
an unrecognized (non-HTTP) protocol.
37+
</p>
38+
</Content>
39+
<Content>
40+
<p>
41+
The connection was made from <CopyableMonoValue>{
42+
sourceIp
43+
}:{
44+
tunnel.remotePort
45+
}</CopyableMonoValue> { sourceDetails } to <CopyableMonoValue>{
46+
tunnel.upstreamHostname
47+
}:{
48+
tunnel.upstreamPort
49+
}</CopyableMonoValue>.
50+
</p>
51+
</Content>
52+
</MediumCard>
53+
<StreamMessageListCard
54+
isPaidUser={this.props.isPaidUser}
55+
ariaLabel='Raw tunnel data'
56+
57+
collapsed={false}
58+
expanded={true}
59+
onExpandToggled={() => {}}
60+
onCollapseToggled={() => {}}
61+
62+
streamId={tunnel.id}
63+
cardHeading='Raw Data'
64+
streamLabel={tunnel.upstreamHostname}
65+
editorNode={this.props.streamMessageEditor}
66+
filenamePrefix={`Raw Tunnel ${tunnel.upstreamHostname} ${tunnel.id}`}
67+
messages={tunnel.packets}
68+
/>
69+
70+
</PaneScrollContainer>;
71+
}
72+
}

src/components/view/rtc/rtc-data-channel-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const RTCDataChannelCard = observer(({
2929
// reset when we switch between traffic:
3030
key={dataChannel.id}
3131
streamId={dataChannel.id}
32-
streamType='DataChannel'
32+
cardHeading='DataChannel Messages'
3333
streamLabel={dataChannel.label}
3434

3535
editorNode={streamMessageEditor}

src/components/view/stream-message-list-card.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,12 @@ function getFilename(
3434
}.${extension}`;
3535
}
3636

37-
export type StreamType = 'WebSocket' | 'DataChannel';
38-
3937
@observer
4038
export class StreamMessageListCard extends React.Component<ExpandableCardProps & {
4139
isPaidUser: boolean,
4240
filenamePrefix: string,
4341
streamId: string,
44-
streamType: StreamType,
42+
cardHeading: string,
4543
streamLabel?: string,
4644
messages: ReadonlyArray<StreamMessage>,
4745
editorNode: portals.HtmlPortalNode<typeof SelfSizedEditor>
@@ -59,7 +57,7 @@ export class StreamMessageListCard extends React.Component<ExpandableCardProps &
5957
render() {
6058
const {
6159
streamId,
62-
streamType,
60+
cardHeading,
6361
streamLabel,
6462
messages,
6563
isPaidUser,
@@ -88,7 +86,7 @@ export class StreamMessageListCard extends React.Component<ExpandableCardProps &
8886
icon={['fas', 'download']}
8987
title={
9088
isPaidUser
91-
? "Save these message as a file"
89+
? "Save these messages as a file"
9290
: "With Pro: Save these messages as a file"
9391
}
9492
disabled={!isPaidUser}
@@ -115,7 +113,7 @@ export class StreamMessageListCard extends React.Component<ExpandableCardProps &
115113
<CollapsibleCardHeading
116114
onCollapseToggled={onCollapseToggled}
117115
>
118-
{ streamType } messages
116+
{ cardHeading }
119117
</CollapsibleCardHeading>
120118
</header>
121119
<StreamMessagesList expanded={!!expanded}>

src/components/view/view-event-list.tsx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
RTCStream,
1616
FailedTlsConnection,
1717
RTCConnection,
18-
TlsTunnel
18+
TlsTunnel,
19+
RawTunnel
1920
} from '../../types';
2021
import { UiStore } from '../../model/ui/ui-store';
2122

@@ -381,6 +382,13 @@ const EventRow = observer((props: EventRowProps) => {
381382
style={style}
382383
event={event}
383384
/>;
385+
} else if (event.isRawTunnel()) {
386+
return <RawTunnelRow
387+
index={index}
388+
isSelected={isSelected}
389+
style={style}
390+
event={event}
391+
/>;
384392
} else {
385393
throw new UnreachableCheck(event);
386394
}
@@ -708,6 +716,33 @@ const BuiltInApiRow = observer((p: {
708716
</TrafficEventListRow>
709717
});
710718

719+
const RawTunnelRow = observer((p: {
720+
index: number,
721+
event: RawTunnel,
722+
isSelected: boolean,
723+
style: {}
724+
}) => {
725+
const { event } = p;
726+
727+
const connectionTarget = event.upstreamHostname || 'unknown domain';
728+
729+
return <TlsListRow
730+
role="row"
731+
aria-label={`Non-HTTP connection to ${connectionTarget}`}
732+
aria-rowindex={p.index + 1}
733+
data-event-id={event.id}
734+
tabIndex={p.isSelected ? 0 : -1}
735+
736+
className={p.isSelected ? 'selected' : ''}
737+
style={p.style}
738+
>
739+
{
740+
event.isOpen() &&
741+
<ConnectedSpinnerIcon />
742+
} Non-HTTP connection to { connectionTarget }
743+
</TlsListRow>
744+
});
745+
711746
const TlsRow = observer((p: {
712747
index: number,
713748
tlsEvent: FailedTlsConnection | TlsTunnel,

src/components/view/view-page.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import { observer, disposeOnUnmount, inject } from 'mobx-react';
1414
import * as portals from 'react-reverse-portal';
1515

16-
import { WithInjected, CollectedEvent, HttpExchangeView } from '../../types';
16+
import { WithInjected, CollectedEvent, HttpExchangeView, RawTunnel } from '../../types';
1717
import { NARROW_LAYOUT_BREAKPOINT, styled } from '../../styles';
1818
import { useHotkeys, isEditable, windowSize, AriaCtrlCmd, Ctrl } from '../../util/ui';
1919
import { debounceComputed } from '../../util/observable';
@@ -45,6 +45,7 @@ import { TlsTunnelDetailsPane } from './tls/tls-tunnel-details-pane';
4545
import { RTCDataChannelDetailsPane } from './rtc/rtc-data-channel-details-pane';
4646
import { RTCMediaDetailsPane } from './rtc/rtc-media-details-pane';
4747
import { RTCConnectionDetailsPane } from './rtc/rtc-connection-details-pane';
48+
import { RawTunnelDetailsPane } from './raw-tunnel-details-pane';
4849

4950
interface ViewPageProps {
5051
className?: string;
@@ -404,6 +405,12 @@ class ViewPage extends React.Component<ViewPageProps> {
404405
answerEditor={this.editors.response}
405406
navigate={this.props.navigate}
406407
/>
408+
} else if (this.selectedEvent.isRawTunnel()) {
409+
rightPane = <RawTunnelDetailsPane
410+
tunnel={this.selectedEvent}
411+
streamMessageEditor={this.editors.streamMessage}
412+
isPaidUser={isPaidUser}
413+
/>
407414
} else {
408415
throw new UnreachableCheck(this.selectedEvent);
409416
}

src/model/events/event-base.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
WebSocketView,
88
RTCConnection,
99
RTCDataChannel,
10-
RTCMediaTrack
10+
RTCMediaTrack,
11+
RawTunnel
1112
} from '../../types';
1213

1314
import { getEventCategory } from './categorization';
@@ -22,6 +23,7 @@ export abstract class HTKEventBase {
2223

2324
isTlsFailure(): this is FailedTlsConnection { return false; }
2425
isTlsTunnel(): this is TlsTunnel { return false; }
26+
isRawTunnel(): this is RawTunnel { return false; }
2527

2628
isRTCConnection(): this is RTCConnection { return false; }
2729
isRTCDataChannel(): this is RTCDataChannel { return false; }

src/model/events/events-store.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
InputRTCMediaTrackClosed,
3030
InputRTCExternalPeerAttached,
3131
InputRuleEvent,
32+
InputRawPassthrough,
33+
InputRawPassthroughData,
3234
InputRequest
3335
} from '../../types';
3436

@@ -46,6 +48,7 @@ import { parseHar } from '../http/har';
4648

4749
import { FailedTlsConnection } from '../tls/failed-tls-connection';
4850
import { TlsTunnel } from '../tls/tls-tunnel';
51+
import { RawTunnel } from '../raw-tunnel';
4952
import { HttpExchange } from '../http/http-exchange';
5053
import { WebSocketStream } from '../websockets/websocket-stream';
5154
import { RTCConnection } from '../webrtc/rtc-connection';
@@ -71,6 +74,9 @@ type EventTypesMap = {
7174
'tls-passthrough-opened': InputTlsPassthrough,
7275
'tls-passthrough-closed': InputTlsPassthrough,
7376
'client-error': InputClientError,
77+
'raw-passthrough-opened': InputRawPassthrough,
78+
'raw-passthrough-closed': InputRawPassthrough,
79+
'raw-passthrough-data': InputRawPassthroughData,
7480
'rule-event': InputRuleEvent
7581
} & {
7682
// MockRTC:
@@ -91,6 +97,9 @@ const mockttpEventTypes = [
9197
'tls-passthrough-opened',
9298
'tls-passthrough-closed',
9399
'client-error',
100+
'raw-passthrough-opened',
101+
'raw-passthrough-closed',
102+
'raw-passthrough-data',
94103
'rule-event'
95104
] as const;
96105

@@ -137,6 +146,8 @@ type OrphanableQueuedEvent<T extends
137146
| 'media-track-stats'
138147
| 'media-track-closed'
139148
| 'tls-passthrough-closed'
149+
| 'raw-passthrough-closed'
150+
| 'raw-passthrough-data'
140151
> = { type: T, event: EventTypesMap[T] };
141152

142153
const LARGE_QUEUE_BATCH_SIZE = 1033; // Off by 33 for a new ticking UI effect
@@ -255,6 +266,13 @@ export class EventsStore {
255266
case 'client-error':
256267
return this.addClientError(queuedEvent.event);
257268

269+
case 'raw-passthrough-opened':
270+
return this.addRawTunnel(queuedEvent.event);
271+
case 'raw-passthrough-closed':
272+
return this.markRawTunnelClosed(queuedEvent.event);
273+
case 'raw-passthrough-data':
274+
return this.addRawTunnelChunk(queuedEvent.event);
275+
258276
case 'rule-event':
259277
return this.addRuleEvent(queuedEvent.event);
260278

@@ -453,6 +471,42 @@ export class EventsStore {
453471
tunnel.markClosed(closeEvent);
454472
}
455473

474+
@action
475+
private addRawTunnel(openEvent: InputRawPassthrough) {
476+
const tunnel = new RawTunnel(openEvent);
477+
this.eventsList.push(tunnel);
478+
}
479+
480+
@action
481+
private markRawTunnelClosed(closeEvent: InputRawPassthrough) {
482+
const tunnel = this.eventsList.getRawTunnelById(closeEvent.id);
483+
484+
if (!tunnel) {
485+
// Handle this later, once the tunnel open event has arrived
486+
this.orphanedEvents[closeEvent.id] = {
487+
type: 'raw-passthrough-closed', event: closeEvent
488+
};
489+
return;
490+
}
491+
492+
tunnel.markClosed(closeEvent);
493+
}
494+
495+
@action
496+
private addRawTunnelChunk(dataEvent: InputRawPassthroughData) {
497+
const tunnel = this.eventsList.getRawTunnelById(dataEvent.id);
498+
499+
if (!tunnel) {
500+
// Handle this later, once the tunnel open event has arrived
501+
this.orphanedEvents[dataEvent.id] = {
502+
type: 'raw-passthrough-data', event: dataEvent
503+
};
504+
return;
505+
}
506+
507+
tunnel.addChunk(dataEvent);
508+
}
509+
456510
@action
457511
private addFailedTlsRequest(request: InputTlsFailure) {
458512
if (this.tlsFailures.some((failure) =>

src/model/events/observable-events-list.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
RTCConnection,
1111
RTCDataChannel,
1212
RTCMediaTrack,
13+
RawTunnel,
1314
TlsTunnel,
1415
WebSocketStream
1516
} from '../../types';
@@ -109,6 +110,7 @@ export class ObservableEventsList {
109110
getExchangeById = (id: string) => this.getById(id, (event): event is HttpExchange => event.isHttp());
110111
getWebSocketById = (id: string) => this.getById(id, (event): event is WebSocketStream => event.isWebSocket());
111112
getTlsTunnelById = (id: string) => this.getById(id, (event): event is TlsTunnel => event.isTlsTunnel());
113+
getRawTunnelById = (id: string) => this.getById(id, (event): event is RawTunnel => event.isRawTunnel());
112114
getRTCConnectionById = (id: string) => this.getById(id, (event): event is RTCConnection => event.isRTCConnection());
113115

114116
clear() {

src/model/proxy-store.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ export class ProxyStore {
190190
http2: this._http2CurrentlyEnabled,
191191
https: {
192192
tlsPassthrough: this._currentTlsPassthroughConfig
193-
} as MockttpHttpsOptions // Cert/Key options are set by the server
193+
} as MockttpHttpsOptions, // Cert/Key options are set by the server
194+
socks: true,
195+
passthrough: this.accountStore.featureFlags.includes('raw-tunnels')
196+
? ['unknown-protocol']
197+
: undefined
194198
},
195199
port: this.portConfig,
196200
// We manage our own decoding client side - rules should not try to send

0 commit comments

Comments
 (0)