|
|
@@ -1,20 +1,19 @@
|
|
1
|
1
|
// mtglib defines a package with MTPROTO proxy.
|
|
2
|
2
|
//
|
|
3
|
|
-// Since mtg itself is build as an example of how to work with mtglib,
|
|
4
|
|
-// it worth to telling a couple of words about a project organization.
|
|
|
3
|
+// Since mtg itself is build as an example of how to work with mtglib, it worth
|
|
|
4
|
+// to telling a couple of words about a project organization.
|
|
5
|
5
|
//
|
|
6
|
|
-// A core object of the project is mtglib.Proxy. This is a proxy you
|
|
7
|
|
-// expect: that one which you configure, set to serve on a listener
|
|
8
|
|
-// and/or shutdown on application termination.
|
|
|
6
|
+// A core object of the project is [mtglib.Proxy]. This is a proxy you expect:
|
|
|
7
|
+// that one which you configure, set to serve on a listener and/or shutdown on
|
|
|
8
|
+// application termination.
|
|
9
|
9
|
//
|
|
10
|
|
-// But it also has a core logic unrelated to Telegram per se: anti
|
|
11
|
|
-// replay cache, network connectivity (who knows, maybe you want to have
|
|
12
|
|
-// a native VMESS integration) and so on.
|
|
|
10
|
+// But it also has a core logic unrelated to Telegram per se: anti replay
|
|
|
11
|
+// cache, network connectivity (who knows, maybe you want to have a native
|
|
|
12
|
+// VMESS integration) and so on.
|
|
13
|
13
|
//
|
|
14
|
|
-// You can supply such parts to a proxy with interfaces. The rest of
|
|
15
|
|
-// the packages in mtg define some default implementations of these
|
|
16
|
|
-// interfaces. But if you want to integrate it with, let say, influxdb,
|
|
17
|
|
-// you can do it easily.
|
|
|
14
|
+// You can supply such parts to a proxy with interfaces. The rest of the
|
|
|
15
|
+// packages in mtg define some default implementations of these interfaces. But
|
|
|
16
|
+// if you want to integrate it with, let say, influxdb, you can do it easily.
|
|
18
|
17
|
package mtglib
|
|
19
|
18
|
|
|
20
|
19
|
import (
|
|
|
@@ -28,42 +27,42 @@ import (
|
|
28
|
27
|
)
|
|
29
|
28
|
|
|
30
|
29
|
var (
|
|
31
|
|
- // ErrSecretEmpty is returned if you are trying to create a proxy
|
|
32
|
|
- // but do not provide a secret.
|
|
|
30
|
+ // ErrSecretEmpty is returned if you are trying to create a proxy but do not
|
|
|
31
|
+ // provide a secret.
|
|
33
|
32
|
ErrSecretEmpty = errors.New("secret is empty")
|
|
34
|
33
|
|
|
35
|
|
- // ErrSecretInvalid is returned if you are trying to create a proxy
|
|
36
|
|
- // but secret value is invalid (no host or payload are zeroes).
|
|
|
34
|
+ // ErrSecretInvalid is returned if you are trying to create a proxy but secret
|
|
|
35
|
+ // value is invalid (no host or payload are zeroes).
|
|
37
|
36
|
ErrSecretInvalid = errors.New("secret is invalid")
|
|
38
|
37
|
|
|
39
|
|
- // ErrNetworkIsNotDefined is returned if you are trying to create a
|
|
40
|
|
- // proxy but network value is undefined.
|
|
|
38
|
+ // ErrNetworkIsNotDefined is returned if you are trying to create a proxy but
|
|
|
39
|
+ // network value is undefined.
|
|
41
|
40
|
ErrNetworkIsNotDefined = errors.New("network is not defined")
|
|
42
|
41
|
|
|
43
|
|
- // ErrAntiReplayCacheIsNotDefined is returned if you are trying to
|
|
44
|
|
- // create a proxy but anti replay cache value is undefined.
|
|
|
42
|
+ // ErrAntiReplayCacheIsNotDefined is returned if you are trying to create a
|
|
|
43
|
+ // proxy but anti replay cache value is undefined.
|
|
45
|
44
|
ErrAntiReplayCacheIsNotDefined = errors.New("anti-replay cache is not defined")
|
|
46
|
45
|
|
|
47
|
|
- // ErrIPBlocklistIsNotDefined is returned if you are trying to
|
|
48
|
|
- // create a proxy but ip blocklist instance is not defined.
|
|
|
46
|
+ // ErrIPBlocklistIsNotDefined is returned if you are trying to create a proxy
|
|
|
47
|
+ // but ip blocklist instance is not defined.
|
|
49
|
48
|
ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
|
|
50
|
49
|
|
|
51
|
|
- // ErrIPAllowlistIsNotDefined is returned if you are trying to
|
|
52
|
|
- // create a proxy but ip allowlist instance is not defined.
|
|
|
50
|
+ // ErrIPAllowlistIsNotDefined is returned if you are trying to create a proxy
|
|
|
51
|
+ // but ip allowlist instance is not defined.
|
|
53
|
52
|
ErrIPAllowlistIsNotDefined = errors.New("ip allowlist is not defined")
|
|
54
|
53
|
|
|
55
|
|
- // ErrEventStreamIsNotDefined is returned if you are trying to create a
|
|
56
|
|
- // proxy but event stream instance is not defined.
|
|
|
54
|
+ // ErrEventStreamIsNotDefined is returned if you are trying to create a proxy
|
|
|
55
|
+ // but event stream instance is not defined.
|
|
57
|
56
|
ErrEventStreamIsNotDefined = errors.New("event stream is not defined")
|
|
58
|
57
|
|
|
59
|
|
- // ErrLoggerIsNotDefined is returned if you are trying to
|
|
60
|
|
- // create a proxy but logger is not defined.
|
|
|
58
|
+ // ErrLoggerIsNotDefined is returned if you are trying to create a proxy but
|
|
|
59
|
+ // logger is not defined.
|
|
61
|
60
|
ErrLoggerIsNotDefined = errors.New("logger is not defined")
|
|
62
|
61
|
)
|
|
63
|
62
|
|
|
64
|
63
|
const (
|
|
65
|
|
- // DefaultConcurrency is a default max count of simultaneously
|
|
66
|
|
- // connected clients.
|
|
|
64
|
+ // DefaultConcurrency is a default max count of simultaneously connected
|
|
|
65
|
+ // clients.
|
|
67
|
66
|
DefaultConcurrency = 4096
|
|
68
|
67
|
|
|
69
|
68
|
// DefaultBufferSize is a default size of a copy buffer.
|
|
|
@@ -71,31 +70,29 @@ const (
|
|
71
|
70
|
// Deprecated: this setting no longer makes any effect.
|
|
72
|
71
|
DefaultBufferSize = 16 * 1024 // 16 kib
|
|
73
|
72
|
|
|
74
|
|
- // DefaultDomainFrontingPort is a default port (HTTPS) to connect to in
|
|
75
|
|
- // case of probe-resistance activity.
|
|
|
73
|
+ // DefaultDomainFrontingPort is a default port (HTTPS) to connect to in case
|
|
|
74
|
+ // of probe-resistance activity.
|
|
76
|
75
|
DefaultDomainFrontingPort = 443
|
|
77
|
76
|
|
|
78
|
|
- // DefaultIdleTimeout is a default timeout for closing a connection
|
|
79
|
|
- // in case of idling.
|
|
|
77
|
+ // DefaultIdleTimeout is a default timeout for closing a connection in case of
|
|
|
78
|
+ // idling.
|
|
80
|
79
|
//
|
|
81
|
|
- // Deprecated: no longer in use because of changed TCP relay
|
|
82
|
|
- // algorithm.
|
|
|
80
|
+ // Deprecated: no longer in use because of changed TCP relay algorithm.
|
|
83
|
81
|
DefaultIdleTimeout = time.Minute
|
|
84
|
82
|
|
|
85
|
|
- // DefaultTolerateTimeSkewness is a default timeout for time
|
|
86
|
|
- // skewness on a faketls timeout verification.
|
|
|
83
|
+ // DefaultTolerateTimeSkewness is a default timeout for time skewness on a
|
|
|
84
|
+ // faketls timeout verification.
|
|
87
|
85
|
DefaultTolerateTimeSkewness = 3 * time.Second
|
|
88
|
86
|
|
|
89
|
|
- // DefaultPreferIP is a default value for Telegram IP connectivity
|
|
90
|
|
- // preference.
|
|
|
87
|
+ // DefaultPreferIP is a default value for Telegram IP connectivity preference.
|
|
91
|
88
|
DefaultPreferIP = "prefer-ipv6"
|
|
92
|
89
|
|
|
93
|
|
- // SecretKeyLength defines a length of the secret bytes used
|
|
94
|
|
- // by Telegram and a proxy.
|
|
|
90
|
+ // SecretKeyLength defines a length of the secret bytes used by Telegram and a
|
|
|
91
|
+ // proxy.
|
|
95
|
92
|
SecretKeyLength = 16
|
|
96
|
93
|
|
|
97
|
|
- // ConnectionIDBytesLength defines a count of random bytes used to generate
|
|
98
|
|
- // a stream/connection ids.
|
|
|
94
|
+ // ConnectionIDBytesLength defines a count of random bytes used to generate a
|
|
|
95
|
+ // stream/connection ids.
|
|
99
|
96
|
ConnectionIDBytesLength = 16
|
|
100
|
97
|
|
|
101
|
98
|
// TCPRelayReadTimeout defines a max time period between two consecuitive
|
|
|
@@ -104,81 +101,76 @@ const (
|
|
104
|
101
|
TCPRelayReadTimeout = 20 * time.Second
|
|
105
|
102
|
)
|
|
106
|
103
|
|
|
107
|
|
-// Network defines a knowledge how to work with a network. It may sound
|
|
108
|
|
-// fun but it encapsulates all the knowledge how to properly establish
|
|
109
|
|
-// connections to remote hosts and configure HTTP clients.
|
|
|
104
|
+// Network defines a knowledge how to work with a network. It may sound fun but
|
|
|
105
|
+// it encapsulates all the knowledge how to properly establish connections to
|
|
|
106
|
+// remote hosts and configure HTTP clients.
|
|
110
|
107
|
//
|
|
111
|
|
-// For example, if you want to use SOCKS5 proxy, you probably want to
|
|
112
|
|
-// have all traffic routed to this proxy: telegram connections, http
|
|
113
|
|
-// requests and so on. This knowledge is encapsulated into instances of
|
|
114
|
|
-// such interface.
|
|
|
108
|
+// For example, if you want to use SOCKS5 proxy, you probably want to have all
|
|
|
109
|
+// traffic routed to this proxy: telegram connections, http requests and so on.
|
|
|
110
|
+// This knowledge is encapsulated into instances of such interface.
|
|
115
|
111
|
//
|
|
116
|
112
|
// mtglib uses Network for:
|
|
117
|
|
-//
|
|
118
|
|
-// 1. Dialing to Telegram
|
|
119
|
|
-//
|
|
120
|
|
-// 2. Dialing to front domain
|
|
121
|
|
-//
|
|
122
|
|
-// 3. Doing HTTP requests (for example, for FireHOL ipblocklist).
|
|
|
113
|
+// 1. Dialing to Telegram
|
|
|
114
|
+// 2. Dialing to front domain
|
|
|
115
|
+// 3. Doing HTTP requests (for example, for FireHOL ipblocklist).
|
|
123
|
116
|
type Network interface {
|
|
124
|
117
|
// Dial establishes context-free TCP connections.
|
|
125
|
118
|
Dial(network, address string) (essentials.Conn, error)
|
|
126
|
119
|
|
|
127
|
|
- // DialContext dials using a context. This is a preferrable
|
|
128
|
|
- // way of establishing TCP connections.
|
|
|
120
|
+ // DialContext dials using a context. This is a preferrable way of
|
|
|
121
|
+ // establishing TCP connections.
|
|
129
|
122
|
DialContext(ctx context.Context, network, address string) (essentials.Conn, error)
|
|
130
|
123
|
|
|
131
|
|
- // MakeHTTPClient build an HTTP client with given dial function. If
|
|
132
|
|
- // nothing is provided, then DialContext of this interface is going
|
|
133
|
|
- // to be used.
|
|
|
124
|
+ // MakeHTTPClient build an HTTP client with given dial function. If nothing is
|
|
|
125
|
+ // provided, then DialContext of this interface is going to be used.
|
|
134
|
126
|
MakeHTTPClient(func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client
|
|
135
|
127
|
}
|
|
136
|
128
|
|
|
137
|
|
-// AntiReplayCache is an interface that is used to detect replay attacks
|
|
138
|
|
-// based on some traffic fingerprints.
|
|
|
129
|
+// AntiReplayCache is an interface that is used to detect replay attacks based
|
|
|
130
|
+// on some traffic fingerprints.
|
|
139
|
131
|
//
|
|
140
|
|
-// Replay attacks are probe attacks whose main goal is to identify if
|
|
141
|
|
-// server software can be classified in some way. For example, if you
|
|
142
|
|
-// send some HTTP request to a web server, then you can expect that this
|
|
143
|
|
-// server will respond with HTTP response back.
|
|
|
132
|
+// Replay attacks are probe attacks whose main goal is to identify if server
|
|
|
133
|
+// software can be classified in some way. For example, if you send some HTTP
|
|
|
134
|
+// request to a web server, then you can expect that this server will respond
|
|
|
135
|
+// with HTTP response back.
|
|
144
|
136
|
//
|
|
145
|
|
-// There is a problem though. Let's imagine, that connection is
|
|
146
|
|
-// encrypted. Let's imagine, that it is encrypted with some static key
|
|
147
|
|
-// like ShadowSocks (https://shadowsocks.org/assets/whitepaper.pdf).
|
|
148
|
|
-// In that case, in theory, if you repeat the same bytes, you can get
|
|
149
|
|
-// the same responses. Let's imagine, that you've cracked the key. then
|
|
150
|
|
-// if you send the same bytes, you can decrypt a response and see its
|
|
151
|
|
-// structure. Based on its structure you can identify if this server is
|
|
152
|
|
-// SOCKS5, MTPROTO proxy etc.
|
|
|
137
|
+// There is a problem though. Let's imagine, that connection is encrypted.
|
|
|
138
|
+// Let's imagine, that it is encrypted with some static key like [ShadowSocks].
|
|
|
139
|
+// In that case, in theory, if you repeat the same bytes, you can get the same
|
|
|
140
|
+// responses. Let's imagine, that you've cracked the key. then if you send the
|
|
|
141
|
+// same bytes, you can decrypt a response and see its structure. Based on its
|
|
|
142
|
+// structure you can identify if this server is SOCKS5, MTPROTO proxy etc.
|
|
153
|
143
|
//
|
|
154
|
|
-// This is just one example, maybe not the best or not the most
|
|
155
|
|
-// relevant. In real life, different organizations use such replay
|
|
156
|
|
-// attacks to perform some reverse engineering of the proxy, do some
|
|
157
|
|
-// statical analysis to identify server software.
|
|
|
144
|
+// This is just one example, maybe not the best or not the most relevant. In
|
|
|
145
|
+// real life, different organizations use such replay attacks to perform some
|
|
|
146
|
+// reverse engineering of the proxy, do some statical analysis to identify
|
|
|
147
|
+// server software.
|
|
158
|
148
|
//
|
|
159
|
|
-// There are many ways how to protect your proxy against them. One
|
|
160
|
|
-// is domain fronting which is a core part of mtg. Another one is to
|
|
161
|
|
-// collect some 'handshake fingerprints' and forbid duplication.
|
|
|
149
|
+// There are many ways how to protect your proxy against them. One is domain
|
|
|
150
|
+// fronting which is a core part of mtg. Another one is to collect some
|
|
|
151
|
+// 'handshake fingerprints' and forbid duplication.
|
|
162
|
152
|
//
|
|
163
|
|
-// So, it one is sending the same byte flow right after you (or a couple
|
|
164
|
|
-// of hours after), mtg should detect that and reject this connection
|
|
165
|
|
-// (or redirect to fronting domain).
|
|
|
153
|
+// So, it one is sending the same byte flow right after you (or a couple of
|
|
|
154
|
+// hours after), mtg should detect that and reject this connection (or redirect
|
|
|
155
|
+// to fronting domain).
|
|
|
156
|
+//
|
|
|
157
|
+// [ShadowSocks]: https://shadowsocks.org/assets/whitepaper.pdf
|
|
166
|
158
|
type AntiReplayCache interface {
|
|
167
|
|
- // Seen before checks if this set of bytes was observed before or
|
|
168
|
|
- // not. If it is required to store this information somewhere else,
|
|
169
|
|
- // then it has to do that.
|
|
|
159
|
+ // Seen before checks if this set of bytes was observed before or not. If it
|
|
|
160
|
+ // is required to store this information somewhere else, then it has to do
|
|
|
161
|
+ // that.
|
|
170
|
162
|
SeenBefore(data []byte) bool
|
|
171
|
163
|
}
|
|
172
|
164
|
|
|
173
|
165
|
// IPBlocklist filters requests based on IP address.
|
|
174
|
166
|
//
|
|
175
|
|
-// If this filter has an IP address, then mtg closes a request without
|
|
176
|
|
-// reading anything from a socket. It also does not give such request to
|
|
177
|
|
-// a worker pool, so in worst cases you can expect that you invoke this
|
|
178
|
|
-// object more frequent than defined proxy concurrency.
|
|
|
167
|
+// If this filter has an IP address, then mtg closes a request without reading
|
|
|
168
|
+// anything from a socket. It also does not give such request to a worker pool,
|
|
|
169
|
+// so in worst cases you can expect that you invoke this object more frequent
|
|
|
170
|
+// than defined proxy concurrency.
|
|
179
|
171
|
type IPBlocklist interface {
|
|
180
|
|
- // Contains checks if given IP address belongs to this blocklist If.
|
|
181
|
|
- // it is, a connection is terminated .
|
|
|
172
|
+ // Contains checks if given IP address belongs to this blocklist If. it is, a
|
|
|
173
|
+ // connection is terminated .
|
|
182
|
174
|
Contains(net.IP) bool
|
|
183
|
175
|
|
|
184
|
176
|
// Run starts a background update procedure for a blocklist
|
|
|
@@ -188,40 +180,35 @@ type IPBlocklist interface {
|
|
188
|
180
|
Shutdown()
|
|
189
|
181
|
}
|
|
190
|
182
|
|
|
191
|
|
-// Event is a data structure which is populated during mtg request
|
|
192
|
|
-// processing lifecycle. Each request popluates many events:
|
|
193
|
|
-//
|
|
194
|
|
-// 1. Client connected
|
|
|
183
|
+// Event is a data structure which is populated during mtg request processing
|
|
|
184
|
+// lifecycle. Each request popluates many events:
|
|
|
185
|
+// 1. Client connected
|
|
|
186
|
+// 2. Request is finished
|
|
|
187
|
+// 3. Connection to Telegram server is established
|
|
195
|
188
|
//
|
|
196
|
|
-// 2. Request is finished
|
|
197
|
|
-//
|
|
198
|
|
-// 3. Connection to Telegram server is established
|
|
199
|
|
-//
|
|
200
|
|
-// and so on. All these events are data structures but all of them
|
|
201
|
|
-// must conform the same interface.
|
|
|
189
|
+// and so on. All these events are data structures but all of them must conform
|
|
|
190
|
+// the same interface.
|
|
202
|
191
|
type Event interface {
|
|
203
|
|
- // StreamID returns an identifier of the stream, connection,
|
|
204
|
|
- // request, you name it. All events within the same stream returns
|
|
205
|
|
- // the same stream id.
|
|
|
192
|
+ // StreamID returns an identifier of the stream, connection, request, you name
|
|
|
193
|
+ // it. All events within the same stream returns the same stream id.
|
|
206
|
194
|
StreamID() string
|
|
207
|
195
|
|
|
208
|
196
|
// Timestamp returns a timestamp when this event was generated.
|
|
209
|
197
|
Timestamp() time.Time
|
|
210
|
198
|
}
|
|
211
|
199
|
|
|
212
|
|
-// EventStream is an abstraction that accepts a set of events produced
|
|
213
|
|
-// by mtg. Its main goal is to inject your logging or monitoring system.
|
|
|
200
|
+// EventStream is an abstraction that accepts a set of events produced by mtg.
|
|
|
201
|
+// Its main goal is to inject your logging or monitoring system.
|
|
214
|
202
|
//
|
|
215
|
|
-// The idea is simple. When mtg works, it emits a set of events during
|
|
216
|
|
-// a lifecycle of the requestor: EventStart, EventFinish etc. mtg is a
|
|
217
|
|
-// producer which puts these events into a stream. Responsibility of
|
|
218
|
|
-// the stream is to deliver this event to consumers/observers. There
|
|
219
|
|
-// might be many different observers (for example, you want to have both
|
|
220
|
|
-// statsd and prometheus), mtg should know nothing about them.
|
|
|
203
|
+// The idea is simple. When mtg works, it emits a set of events during a
|
|
|
204
|
+// lifecycle of the requestor: EventStart, EventFinish etc. mtg is a producer
|
|
|
205
|
+// which puts these events into a stream. Responsibility of the stream is to
|
|
|
206
|
+// deliver this event to consumers/observers. There might be many different
|
|
|
207
|
+// observers (for example, you want to have both statsd and prometheus), mtg
|
|
|
208
|
+// should know nothing about them.
|
|
221
|
209
|
type EventStream interface {
|
|
222
|
|
- // Send delivers an event to observers. Given context has to be
|
|
223
|
|
- // respected. If the context is closed, all blocking operations should
|
|
224
|
|
- // be released ASAP.
|
|
|
210
|
+ // Send delivers an event to observers. Given context has to be respected. If
|
|
|
211
|
+ // the context is closed, all blocking operations should be released ASAP.
|
|
225
|
212
|
//
|
|
226
|
213
|
// It is possible that context is closed but the message is delivered.
|
|
227
|
214
|
// EventStream implementations should solve this issue somehow.
|
|
|
@@ -230,27 +217,26 @@ type EventStream interface {
|
|
230
|
217
|
|
|
231
|
218
|
// Logger defines an interface of the logger used by mtglib.
|
|
232
|
219
|
//
|
|
233
|
|
-// Each logger has a name. It is possible to stack names to organize
|
|
234
|
|
-// poor-man namespaces. Also, each logger must be able to bind
|
|
235
|
|
-// parameters to avoid pushing them all the time.
|
|
|
220
|
+// Each logger has a name. It is possible to stack names to organize poor-man
|
|
|
221
|
+// namespaces. Also, each logger must be able to bind parameters to avoid
|
|
|
222
|
+// pushing them all the time.
|
|
236
|
223
|
//
|
|
237
|
224
|
// Example
|
|
238
|
225
|
//
|
|
239
|
|
-// logger := SomeLogger{}
|
|
240
|
|
-// logger = logger.BindStr("ip", net.IP{127, 0, 0, 1})
|
|
241
|
|
-// logger.Info("Hello")
|
|
|
226
|
+// logger := SomeLogger{} logger = logger.BindStr("ip", net.IP{127, 0, 0, 1})
|
|
|
227
|
+// logger.Info("Hello")
|
|
242
|
228
|
//
|
|
243
|
|
-// In that case, ip is bound as a parameter. It is a great idea to
|
|
244
|
|
-// put this parameter somewhere in a log message.
|
|
|
229
|
+// In that case, ip is bound as a parameter. It is a great idea to put this
|
|
|
230
|
+// parameter somewhere in a log message.
|
|
245
|
231
|
//
|
|
246
|
|
-// logger1 = logger.BindStr("param1", "11")
|
|
247
|
|
-// logger2 = logger.BindInt("param2", 11)
|
|
|
232
|
+// logger1 = logger.BindStr("param1", "11") logger2 = logger.BindInt("param2",
|
|
|
233
|
+// 11)
|
|
248
|
234
|
//
|
|
249
|
235
|
// logger1 should see no param2 and vice versa, logger2 should not see param1
|
|
250
|
236
|
// If you attach a parameter to a logger, parents should not know about that.
|
|
251
|
237
|
type Logger interface {
|
|
252
|
|
- // Named returns a new logger with a bound name. Name chaining is
|
|
253
|
|
- // allowed and appreciated.
|
|
|
238
|
+ // Named returns a new logger with a bound name. Name chaining is allowed and
|
|
|
239
|
+ // appreciated.
|
|
254
|
240
|
Named(name string) Logger
|
|
255
|
241
|
|
|
256
|
242
|
// BindInt binds new integer parameter to a new logger instance.
|
|
|
@@ -268,22 +254,21 @@ type Logger interface {
|
|
268
|
254
|
// Info puts a message about some normal situation.
|
|
269
|
255
|
Info(msg string)
|
|
270
|
256
|
|
|
271
|
|
- // InfoError puts a message about some normal situation but this
|
|
272
|
|
- // situation is related to a given error.
|
|
|
257
|
+ // InfoError puts a message about some normal situation but this situation is
|
|
|
258
|
+ // related to a given error.
|
|
273
|
259
|
InfoError(msg string, err error)
|
|
274
|
260
|
|
|
275
|
|
- // Warning puts a message about some extraordinary situation
|
|
276
|
|
- // worth to look at.
|
|
|
261
|
+ // Warning puts a message about some extraordinary situation worth to look at.
|
|
277
|
262
|
Warning(msg string)
|
|
278
|
263
|
|
|
279
|
|
- // WarningError puts a message about some extraordinary situation
|
|
280
|
|
- // worth to look at. This situation is related to a given error.
|
|
|
264
|
+ // WarningError puts a message about some extraordinary situation worth to
|
|
|
265
|
+ // look at. This situation is related to a given error.
|
|
281
|
266
|
WarningError(msg string, err error)
|
|
282
|
267
|
|
|
283
|
268
|
// Debug puts a message useful for debugging only.
|
|
284
|
269
|
Debug(msg string)
|
|
285
|
270
|
|
|
286
|
|
- // Debug puts a message useful for debugging only. This message is
|
|
287
|
|
- // related to a given error.
|
|
|
271
|
+ // Debug puts a message useful for debugging only. This message is related to
|
|
|
272
|
+ // a given error.
|
|
288
|
273
|
DebugError(msg string, err error)
|
|
289
|
274
|
}
|