marge: 合并11.2版本
This commit is contained in:
@@ -44,12 +44,46 @@ type YamlConfig struct {
|
||||
ListenAddr string `yaml:"listenAddr"`
|
||||
ListenPort uint16 `yaml:"listenPort"`
|
||||
PrivateKey string `yaml:"privateKey"`
|
||||
MaxConnNum uint8 `yaml:"maxConnNum"`
|
||||
MaxConnNum int `yaml:"maxConnNum"`
|
||||
Timeout uint16 `yaml:"timeout"`
|
||||
Session string `yaml:"session"`
|
||||
MmlHome string `yaml:"mmlHome"`
|
||||
UserName string `yaml:"userName"`
|
||||
Password string `yaml:"password"`
|
||||
AuthType string `yaml:"authType"`
|
||||
TagNE string `yaml:"tagNE"`
|
||||
} `yaml:"sshd"`
|
||||
|
||||
TelnetServer struct {
|
||||
ListenAddr string `yaml:"listenAddr"`
|
||||
ListenPort uint16 `yaml:"listenPort"`
|
||||
MaxConnNum int `yaml:"maxConnNum"`
|
||||
Timeout uint16 `yaml:"timeout"`
|
||||
Session string `yaml:"session"`
|
||||
MmlHome string `yaml:"mmlHome"`
|
||||
UserName string `yaml:"userName"`
|
||||
Password string `yaml:"password"`
|
||||
AuthType string `yaml:"authType"`
|
||||
TagNE string `yaml:"tagNE"`
|
||||
} `yaml:"telnetServer"`
|
||||
|
||||
SNMPServer struct {
|
||||
ListenAddr string `yaml:"listenAddr"`
|
||||
ListenPort uint16 `yaml:"listenPort"`
|
||||
UserName string `yaml:"userName"`
|
||||
AuthPass string `yaml:"authPass"`
|
||||
AuthProto string `yaml:"authProto"`
|
||||
PrivPass string `yaml:"privPass"`
|
||||
PrivProto string `yaml:"privProto"`
|
||||
EngineID string `yaml:"engineID"`
|
||||
TrapPort uint16 `yaml:"trapPort"`
|
||||
TrapListen bool `yaml:"trapListen"`
|
||||
TrapBool bool `yaml:"trapBool"`
|
||||
TrapTick uint16 `yaml:"trapTick"`
|
||||
TimeOut uint16 `yaml:"timeOut"`
|
||||
TrapTarget string `yaml:"trapTarget"`
|
||||
} `yaml:"snmpServer"`
|
||||
|
||||
Database DbConfig `yaml:"database"`
|
||||
|
||||
OMC struct {
|
||||
@@ -133,14 +167,16 @@ const DefaultConfigFile = "./etc/sshsvc.yaml"
|
||||
func init() {
|
||||
cfile := flag.String("c", DefaultConfigFile, "config file")
|
||||
pv := flag.Bool("v", false, "print version")
|
||||
pversion := flag.Bool("version", false, "print version")
|
||||
ph := flag.Bool("h", false, "print help")
|
||||
phelp := flag.Bool("help", false, "print help")
|
||||
|
||||
flag.Parse()
|
||||
if *pv {
|
||||
if *pv || *pversion {
|
||||
fmt.Printf("OMC sshsvc version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
|
||||
os.Exit(0)
|
||||
}
|
||||
if *ph {
|
||||
if *ph || *phelp {
|
||||
flag.Usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# duration: rotation time with xx hours, example: 1/12/24 hours
|
||||
# count: rotation count of log, default is 30 rotation
|
||||
logger:
|
||||
file: d:/local.git/be.ems/sshsvc/log/sshsvc.log
|
||||
file: d:/omc.git/be.ems/sshsvc/log/sshsvc.log
|
||||
level: trace
|
||||
duration: 24
|
||||
count: 30
|
||||
@@ -13,7 +13,7 @@ logger:
|
||||
# count: rotation count of log, default is 30 rotation
|
||||
# level: cmd/ret log cmd/log cmd & result
|
||||
logmml:
|
||||
file: d:/local.git/be.ems/sshsvc/mmllog/omcmml.log
|
||||
file: d:/omc.git/be.ems/sshsvc/mmllog/omcmml.log
|
||||
duration: 24
|
||||
count: 30
|
||||
level: ret
|
||||
@@ -21,22 +21,59 @@ logmml:
|
||||
# ssh service listen ipv4/v6 and port, support multiple routines
|
||||
# ip: 0.0.0.0 or ::0, support IPv4/v6
|
||||
# session: single/multiple session for one user
|
||||
# authType: local/radius
|
||||
sshd:
|
||||
listenAddr: 0.0.0.0
|
||||
listenPort: 2222
|
||||
listenPort: 32222
|
||||
privateKey: ./.ssh/id_rsa
|
||||
maxConnNum: 20
|
||||
maxConnNum: 2
|
||||
timeout: 1800
|
||||
session: multiple
|
||||
mmlHome: ./mmlhome
|
||||
userName: manager
|
||||
password: pass123
|
||||
authType: local
|
||||
tagNE: hlr
|
||||
|
||||
# authType: local/omc
|
||||
telnetServer:
|
||||
listenAddr: 0.0.0.0
|
||||
listenPort: 32323
|
||||
maxConnNum: 2
|
||||
timeout: 1800
|
||||
session: multiple
|
||||
mmlHome: ./mmlhome
|
||||
userName: manager
|
||||
password: pass123
|
||||
authType: local
|
||||
tagNE: hlr
|
||||
|
||||
# authproto: NoAuth/MD5/SHA
|
||||
# privProto: NoPriv/DES/AES/AES192/AES256
|
||||
snmpServer:
|
||||
listenAddr: '[::]'
|
||||
listenPort: 34957
|
||||
userName: manager
|
||||
authPass: pass123
|
||||
authproto: MD5
|
||||
privPass: "3F2A1B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3D4E"
|
||||
privProto: DES
|
||||
#engineID: "800007db03360102101101"
|
||||
engineID: "8000000004323030313a6462383a3a39313636"
|
||||
trapPort: 34958
|
||||
trapListen: false
|
||||
trapBool: false
|
||||
trapTick: 60
|
||||
timeOut: 5
|
||||
trapTarget: "2001:db8::9219"
|
||||
|
||||
database:
|
||||
type: mysql
|
||||
user: root
|
||||
password: 1000omc@kp!
|
||||
host: 127.0.0.1
|
||||
port: 33066
|
||||
name: omc_db
|
||||
user: administrator
|
||||
password: "*86#ROtartsinim"
|
||||
host: 192.168.13.117
|
||||
port: 3306
|
||||
name: OMC_PUB
|
||||
connParam: charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&interpolateParams=True
|
||||
|
||||
omc:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Makefile for OMC-OMC-crontask project
|
||||
|
||||
PROJECT = OMC
|
||||
VERSION = 2.2408.1
|
||||
VERSION = 2.2411.2
|
||||
LIBDIR = be.ems/lib
|
||||
BINNAME = sshsvc
|
||||
|
||||
|
||||
7567
sshsvc/mibs/CINTEL-HLR-MIB.my
Normal file
7567
sshsvc/mibs/CINTEL-HLR-MIB.my
Normal file
File diff suppressed because it is too large
Load Diff
BIN
sshsvc/mibs/CINTEL-HLR-MIB.smidb
Normal file
BIN
sshsvc/mibs/CINTEL-HLR-MIB.smidb
Normal file
Binary file not shown.
656
sshsvc/mibs/CINTEL-MIB.my
Normal file
656
sshsvc/mibs/CINTEL-MIB.my
Normal file
@@ -0,0 +1,656 @@
|
||||
--
|
||||
-- CINTEL-MIB.my
|
||||
-- MIB generated by MG-SOFT Visual MIB Builder Version 7.0 Build 209
|
||||
-- Friday, December 16, 2011 at 15:52:13
|
||||
--
|
||||
|
||||
CINTEL-MIB DEFINITIONS ::= BEGIN
|
||||
|
||||
IMPORTS
|
||||
enterprises, TimeTicks, MODULE-IDENTITY
|
||||
FROM SNMPv2-SMI
|
||||
TEXTUAL-CONVENTION
|
||||
FROM SNMPv2-TC;
|
||||
|
||||
|
||||
-- 1.3.6.1.4.1.1379.2
|
||||
cintelSS MODULE-IDENTITY
|
||||
LAST-UPDATED "201706041222Z" -- June 04, 2017 at 12:22 GMT
|
||||
ORGANIZATION
|
||||
"CINTEL"
|
||||
CONTACT-INFO
|
||||
"cintel
|
||||
support@cintel.com.cn"
|
||||
DESCRIPTION
|
||||
"The MIB module for cintel's Softswitch products."
|
||||
REVISION "201706041223Z" -- June 04, 2017 at 12:23 GMT
|
||||
DESCRIPTION
|
||||
"This is the first release version of the MIB"
|
||||
::= { cintelNS 2 }
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Type definitions
|
||||
--
|
||||
|
||||
AdminStateChoices ::= INTEGER
|
||||
{
|
||||
locked(0),
|
||||
unlocked(1),
|
||||
shutDown(2)
|
||||
}
|
||||
|
||||
OperStateChoices ::= INTEGER
|
||||
{
|
||||
disable(0),
|
||||
enable(1)
|
||||
}
|
||||
|
||||
AvailStateChoices ::= INTEGER
|
||||
{
|
||||
inTest(0),
|
||||
failed(1),
|
||||
powerOff(2),
|
||||
offLine(3),
|
||||
onLine(4),
|
||||
dependency(5),
|
||||
degraded(6),
|
||||
notInstalled(7)
|
||||
}
|
||||
|
||||
|
||||
--
|
||||
-- Textual conventions
|
||||
--
|
||||
|
||||
-- TEXTUAL-CONVENTION MACRO ::=
|
||||
-- BEGIN
|
||||
-- TYPE NOTATION ::=
|
||||
-- DisplayPart
|
||||
-- "STATUS" Status
|
||||
-- "DESCRIPTION" Text
|
||||
-- ReferPart
|
||||
-- "SYNTAX" Syntax
|
||||
--
|
||||
-- VALUE NOTATION ::=
|
||||
-- value(VALUE Syntax)
|
||||
--
|
||||
-- DisplayPart ::=
|
||||
-- "DISPLAY-HINT" Text
|
||||
-- | empty
|
||||
--
|
||||
-- Status ::=
|
||||
-- "current"
|
||||
-- | "deprecated"
|
||||
-- | "obsolete"
|
||||
--
|
||||
-- ReferPart ::=
|
||||
-- "REFERENCE" Text
|
||||
-- | empty
|
||||
--
|
||||
-- -- -- uses the NVT ASCII character set
|
||||
-- Text ::= """" string """"
|
||||
--
|
||||
-- Syntax ::=
|
||||
-- type(ObjectSyntax)
|
||||
-- | "BITS" "{" Kibbles "}"
|
||||
-- Kibbles ::=
|
||||
-- Kibble
|
||||
-- | Kibbles "," Kibble
|
||||
-- Kibble ::=
|
||||
-- identifier "(" nonNegativeNumber ")"
|
||||
-- END
|
||||
DisplayString8 ::= TEXTUAL-CONVENTION
|
||||
DISPLAY-HINT
|
||||
"8a"
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"Represents textual information taken from the NVT ASCII
|
||||
character set, as defined in pages 4, 10-11 of RFC 854.
|
||||
|
||||
To summarize RFC 854, the NVT ASCII repertoire specifies:
|
||||
|
||||
- the use of character codes 0-127 (decimal)
|
||||
|
||||
- the graphics characters (32-126) are interpreted as
|
||||
US ASCII
|
||||
|
||||
- NUL, LF, CR, BEL, BS, HT, VT and FF have the special
|
||||
meanings specified in RFC 854
|
||||
|
||||
- the other 25 codes have no standard interpretation
|
||||
|
||||
- the sequence 'CR LF' means newline
|
||||
|
||||
- the sequence 'CR NUL' means carriage-return
|
||||
|
||||
- an 'LF' not preceded by a 'CR' means moving to the
|
||||
same column on the next line.
|
||||
|
||||
- the sequence 'CR x' for any x other than LF or NUL is
|
||||
illegal. (Note that this also means that a string may
|
||||
end with either 'CR LF' or 'CR NUL', but not with CR.)
|
||||
|
||||
Any object defined using this syntax may not exceed 255
|
||||
characters in length."
|
||||
SYNTAX OCTET STRING (SIZE (0..8))
|
||||
|
||||
DisplayString16 ::= TEXTUAL-CONVENTION
|
||||
DISPLAY-HINT
|
||||
"16a"
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"A version of DisplayString that contains only 16 characters most."
|
||||
SYNTAX OCTET STRING (SIZE (0..16))
|
||||
|
||||
DisplayString32 ::= TEXTUAL-CONVENTION
|
||||
DISPLAY-HINT
|
||||
"32a"
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"A version of DisplayString that contains only 32 characters most."
|
||||
SYNTAX OCTET STRING (SIZE (0..32))
|
||||
|
||||
DisplayString64 ::= TEXTUAL-CONVENTION
|
||||
DISPLAY-HINT
|
||||
"64a"
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"A version of DisplayString that contains only 64 characters most."
|
||||
SYNTAX OCTET STRING (SIZE (0..64))
|
||||
|
||||
DisplayString ::= TEXTUAL-CONVENTION
|
||||
DISPLAY-HINT
|
||||
"255a"
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"A version of DisplayString that contains only 255 characters most."
|
||||
SYNTAX OCTET STRING (SIZE (0..255))
|
||||
|
||||
RowStatus ::= TEXTUAL-CONVENTION
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"The RowStatus textual convention is used to manage the
|
||||
creation and deletion of conceptual rows, and is used as the
|
||||
value of the SYNTAX clause for the status column of a
|
||||
conceptual row (as described in Section 7.7.1 of [2].)
|
||||
|
||||
The status column has six defined values:
|
||||
|
||||
- `active', which indicates that the conceptual row is
|
||||
available for use by the managed device;
|
||||
|
||||
- `notInService', which indicates that the conceptual
|
||||
row exists in the agent, but is unavailable for use by
|
||||
the managed device (see NOTE below);
|
||||
|
||||
- `notReady', which indicates that the conceptual row
|
||||
exists in the agent, but is missing information
|
||||
necessary in order to be available for use by the
|
||||
managed device;
|
||||
|
||||
- `createAndGo', which is supplied by a management
|
||||
station wishing to create a new instance of a
|
||||
conceptual row and to have its status automatically set
|
||||
to active, making it available for use by the managed
|
||||
device;
|
||||
|
||||
- `createAndWait', which is supplied by a management
|
||||
station wishing to create a new instance of a
|
||||
conceptual row (but not make it available for use by
|
||||
the managed device); and,
|
||||
|
||||
- `destroy', which is supplied by a management station
|
||||
wishing to delete all of the instances associated with
|
||||
an existing conceptual row.
|
||||
|
||||
Whereas five of the six values (all except `notReady') may
|
||||
be specified in a management protocol set operation, only
|
||||
three values will be returned in response to a management
|
||||
protocol retrieval operation: `notReady', `notInService' or
|
||||
`active'. That is, when queried, an existing conceptual row
|
||||
has only three states: it is either available for use by
|
||||
the managed device (the status column has value `active');
|
||||
it is not available for use by the managed device, though
|
||||
the agent has sufficient information to make it so (the
|
||||
status column has value `notInService'); or, it is not
|
||||
available for use by the managed device, and an attempt to
|
||||
make it so would fail because the agent has insufficient
|
||||
information (the state column has value `notReady').
|
||||
|
||||
NOTE WELL
|
||||
|
||||
This textual convention may be used for a MIB table,
|
||||
irrespective of whether the values of that table's
|
||||
conceptual rows are able to be modified while it is
|
||||
active, or whether its conceptual rows must be taken
|
||||
out of service in order to be modified. That is, it is
|
||||
the responsibility of the DESCRIPTION clause of the
|
||||
status column to specify whether the status column must
|
||||
not be `active' in order for the value of some other
|
||||
column of the same conceptual row to be modified. If
|
||||
such a specification is made, affected columns may be
|
||||
changed by an SNMP set PDU if the RowStatus would not
|
||||
be equal to `active' either immediately before or after
|
||||
processing the PDU. In other words, if the PDU also
|
||||
contained a varbind that would change the RowStatus
|
||||
value, the column in question may be changed if the
|
||||
RowStatus was not equal to `active' as the PDU was
|
||||
received, or if the varbind sets the status to a value
|
||||
other than 'active'.
|
||||
|
||||
|
||||
Also note that whenever any elements of a row exist, the
|
||||
RowStatus column must also exist.
|
||||
|
||||
To summarize the effect of having a conceptual row with a
|
||||
status column having a SYNTAX clause value of RowStatus,
|
||||
consider the following state diagram:
|
||||
|
||||
|
||||
STATE
|
||||
+--------------+-----------+-------------+-------------
|
||||
| A | B | C | D
|
||||
| |status col.|status column|
|
||||
|status column | is | is |status column
|
||||
ACTION |does not exist| notReady | notInService| is active
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set status |noError ->D|inconsist- |inconsistent-|inconsistent-
|
||||
column to | or | entValue| Value| Value
|
||||
createAndGo |inconsistent- | | |
|
||||
| Value| | |
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set status |noError see 1|inconsist- |inconsistent-|inconsistent-
|
||||
column to | or | entValue| Value| Value
|
||||
createAndWait |wrongValue | | |
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set status |inconsistent- |inconsist- |noError |noError
|
||||
column to | Value| entValue| |
|
||||
active | | | |
|
||||
| | or | |
|
||||
| | | |
|
||||
| |see 2 ->D| ->D| ->D
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set status |inconsistent- |inconsist- |noError |noError ->C
|
||||
column to | Value| entValue| |
|
||||
notInService | | | |
|
||||
| | or | | or
|
||||
| | | |
|
||||
| |see 3 ->C| ->C|wrongValue
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set status |noError |noError |noError |noError
|
||||
column to | | | |
|
||||
destroy | ->A| ->A| ->A| ->A
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
set any other |see 4 |noError |noError |see 5
|
||||
column to some| | | |
|
||||
value | | see 1| ->C| ->D
|
||||
--------------+--------------+-----------+-------------+-------------
|
||||
|
||||
(1) goto B or C, depending on information available to the
|
||||
agent.
|
||||
|
||||
(2) if other variable bindings included in the same PDU,
|
||||
provide values for all columns which are missing but
|
||||
required, then return noError and goto D.
|
||||
|
||||
(3) if other variable bindings included in the same PDU,
|
||||
provide values for all columns which are missing but
|
||||
required, then return noError and goto C.
|
||||
|
||||
(4) at the discretion of the agent, the return value may be
|
||||
either:
|
||||
|
||||
inconsistentName: because the agent does not choose to
|
||||
create such an instance when the corresponding
|
||||
RowStatus instance does not exist, or
|
||||
|
||||
inconsistentValue: if the supplied value is
|
||||
inconsistent with the state of some other MIB object's
|
||||
value, or
|
||||
|
||||
noError: because the agent chooses to create the
|
||||
instance.
|
||||
|
||||
If noError is returned, then the instance of the status
|
||||
column must also be created, and the new state is B or C,
|
||||
depending on the information available to the agent. If
|
||||
inconsistentName or inconsistentValue is returned, the row
|
||||
remains in state A.
|
||||
|
||||
(5) depending on the MIB definition for the column/table,
|
||||
either noError or inconsistentValue may be returned.
|
||||
|
||||
NOTE: Other processing of the set request may result in a
|
||||
response other than noError being returned, e.g.,
|
||||
wrongValue, noCreation, etc.
|
||||
|
||||
|
||||
Conceptual Row Creation
|
||||
|
||||
There are four potential interactions when creating a
|
||||
conceptual row: selecting an instance-identifier which is
|
||||
not in use; creating the conceptual row; initializing any
|
||||
objects for which the agent does not supply a default; and,
|
||||
making the conceptual row available for use by the managed
|
||||
device.
|
||||
|
||||
Interaction 1: Selecting an Instance-Identifier
|
||||
|
||||
The algorithm used to select an instance-identifier varies
|
||||
for each conceptual row. In some cases, the instance-
|
||||
identifier is semantically significant, e.g., the
|
||||
destination address of a route, and a management station
|
||||
selects the instance-identifier according to the semantics.
|
||||
|
||||
In other cases, the instance-identifier is used solely to
|
||||
distinguish conceptual rows, and a management station
|
||||
without specific knowledge of the conceptual row might
|
||||
examine the instances present in order to determine an
|
||||
unused instance-identifier. (This approach may be used, but
|
||||
it is often highly sub-optimal; however, it is also a
|
||||
questionable practice for a naive management station to
|
||||
attempt conceptual row creation.)
|
||||
|
||||
Alternately, the MIB module which defines the conceptual row
|
||||
might provide one or more objects which provide assistance
|
||||
in determining an unused instance-identifier. For example,
|
||||
if the conceptual row is indexed by an integer-value, then
|
||||
an object having an integer-valued SYNTAX clause might be
|
||||
defined for such a purpose, allowing a management station to
|
||||
issue a management protocol retrieval operation. In order
|
||||
to avoid unnecessary collisions between competing management
|
||||
stations, `adjacent' retrievals of this object should be
|
||||
different.
|
||||
|
||||
Finally, the management station could select a pseudo-random
|
||||
number to use as the index. In the event that this index
|
||||
was already in use and an inconsistentValue was returned in
|
||||
response to the management protocol set operation, the
|
||||
management station should simply select a new pseudo-random
|
||||
number and retry the operation.
|
||||
|
||||
A MIB designer should choose between the two latter
|
||||
algorithms based on the size of the table (and therefore the
|
||||
efficiency of each algorithm). For tables in which a large
|
||||
number of entries are expected, it is recommended that a MIB
|
||||
object be defined that returns an acceptable index for
|
||||
creation. For tables with small numbers of entries, it is
|
||||
recommended that the latter pseudo-random index mechanism be
|
||||
used.
|
||||
|
||||
|
||||
Interaction 2: Creating the Conceptual Row
|
||||
|
||||
Once an unused instance-identifier has been selected, the
|
||||
management station determines if it wishes to create and
|
||||
activate the conceptual row in one transaction or in a
|
||||
negotiated set of interactions.
|
||||
|
||||
Interaction 2a: Creating and Activating the Conceptual Row
|
||||
|
||||
The management station must first determine the column
|
||||
requirements, i.e., it must determine those columns for
|
||||
which it must or must not provide values. Depending on the
|
||||
complexity of the table and the management station's
|
||||
knowledge of the agent's capabilities, this determination
|
||||
can be made locally by the management station. Alternately,
|
||||
the management station issues a management protocol get
|
||||
operation to examine all columns in the conceptual row that
|
||||
it wishes to create. In response, for each column, there
|
||||
are three possible outcomes:
|
||||
|
||||
- a value is returned, indicating that some other
|
||||
management station has already created this conceptual
|
||||
row. We return to interaction 1.
|
||||
|
||||
- the exception `noSuchInstance' is returned,
|
||||
indicating that the agent implements the object-type
|
||||
associated with this column, and that this column in at
|
||||
least one conceptual row would be accessible in the MIB
|
||||
view used by the retrieval were it to exist. For those
|
||||
columns to which the agent provides read-create access,
|
||||
the `noSuchInstance' exception tells the management
|
||||
station that it should supply a value for this column
|
||||
when the conceptual row is to be created.
|
||||
|
||||
- the exception `noSuchObject' is returned, indicating
|
||||
that the agent does not implement the object-type
|
||||
associated with this column or that there is no
|
||||
conceptual row for which this column would be
|
||||
accessible in the MIB view used by the retrieval. As
|
||||
such, the management station can not issue any
|
||||
management protocol set operations to create an
|
||||
instance of this column.
|
||||
|
||||
Once the column requirements have been determined, a
|
||||
management protocol set operation is accordingly issued.
|
||||
This operation also sets the new instance of the status
|
||||
column to `createAndGo'.
|
||||
|
||||
When the agent processes the set operation, it verifies that
|
||||
it has sufficient information to make the conceptual row
|
||||
available for use by the managed device. The information
|
||||
available to the agent is provided by two sources: the
|
||||
management protocol set operation which creates the
|
||||
conceptual row, and, implementation-specific defaults
|
||||
supplied by the agent (note that an agent must provide
|
||||
implementation-specific defaults for at least those objects
|
||||
which it implements as read-only). If there is sufficient
|
||||
information available, then the conceptual row is created, a
|
||||
`noError' response is returned, the status column is set to
|
||||
`active', and no further interactions are necessary (i.e.,
|
||||
interactions 3 and 4 are skipped). If there is insufficient
|
||||
information, then the conceptual row is not created, and the
|
||||
set operation fails with an error of `inconsistentValue'.
|
||||
On this error, the management station can issue a management
|
||||
protocol retrieval operation to determine if this was
|
||||
because it failed to specify a value for a required column,
|
||||
or, because the selected instance of the status column
|
||||
already existed. In the latter case, we return to
|
||||
interaction 1. In the former case, the management station
|
||||
can re-issue the set operation with the additional
|
||||
information, or begin interaction 2 again using
|
||||
`createAndWait' in order to negotiate creation of the
|
||||
conceptual row.
|
||||
|
||||
NOTE WELL
|
||||
|
||||
Regardless of the method used to determine the column
|
||||
requirements, it is possible that the management
|
||||
station might deem a column necessary when, in fact,
|
||||
the agent will not allow that particular columnar
|
||||
instance to be created or written. In this case, the
|
||||
management protocol set operation will fail with an
|
||||
error such as `noCreation' or `notWritable'. In this
|
||||
case, the management station decides whether it needs
|
||||
to be able to set a value for that particular columnar
|
||||
instance. If not, the management station re-issues the
|
||||
management protocol set operation, but without setting
|
||||
a value for that particular columnar instance;
|
||||
otherwise, the management station aborts the row
|
||||
creation algorithm.
|
||||
|
||||
Interaction 2b: Negotiating the Creation of the Conceptual
|
||||
Row
|
||||
|
||||
The management station issues a management protocol set
|
||||
operation which sets the desired instance of the status
|
||||
column to `createAndWait'. If the agent is unwilling to
|
||||
process a request of this sort, the set operation fails with
|
||||
an error of `wrongValue'. (As a consequence, such an agent
|
||||
must be prepared to accept a single management protocol set
|
||||
operation, i.e., interaction 2a above, containing all of the
|
||||
columns indicated by its column requirements.) Otherwise,
|
||||
the conceptual row is created, a `noError' response is
|
||||
returned, and the status column is immediately set to either
|
||||
`notInService' or `notReady', depending on whether it has
|
||||
sufficient information to make the conceptual row available
|
||||
for use by the managed device. If there is sufficient
|
||||
information available, then the status column is set to
|
||||
`notInService'; otherwise, if there is insufficient
|
||||
information, then the status column is set to `notReady'.
|
||||
Regardless, we proceed to interaction 3.
|
||||
|
||||
Interaction 3: Initializing non-defaulted Objects
|
||||
|
||||
The management station must now determine the column
|
||||
requirements. It issues a management protocol get operation
|
||||
to examine all columns in the created conceptual row. In
|
||||
the response, for each column, there are three possible
|
||||
outcomes:
|
||||
|
||||
- a value is returned, indicating that the agent
|
||||
implements the object-type associated with this column
|
||||
and had sufficient information to provide a value. For
|
||||
those columns to which the agent provides read-create
|
||||
access (and for which the agent allows their values to
|
||||
be changed after their creation), a value return tells
|
||||
the management station that it may issue additional
|
||||
management protocol set operations, if it desires, in
|
||||
order to change the value associated with this column.
|
||||
|
||||
- the exception `noSuchInstance' is returned,
|
||||
indicating that the agent implements the object-type
|
||||
associated with this column, and that this column in at
|
||||
least one conceptual row would be accessible in the MIB
|
||||
view used by the retrieval were it to exist. However,
|
||||
the agent does not have sufficient information to
|
||||
provide a value, and until a value is provided, the
|
||||
conceptual row may not be made available for use by the
|
||||
managed device. For those columns to which the agent
|
||||
provides read-create access, the `noSuchInstance'
|
||||
exception tells the management station that it must
|
||||
issue additional management protocol set operations, in
|
||||
order to provide a value associated with this column.
|
||||
|
||||
- the exception `noSuchObject' is returned, indicating
|
||||
that the agent does not implement the object-type
|
||||
associated with this column or that there is no
|
||||
conceptual row for which this column would be
|
||||
accessible in the MIB view used by the retrieval. As
|
||||
such, the management station can not issue any
|
||||
management protocol set operations to create an
|
||||
instance of this column.
|
||||
|
||||
If the value associated with the status column is
|
||||
`notReady', then the management station must first deal with
|
||||
all `noSuchInstance' columns, if any. Having done so, the
|
||||
value of the status column becomes `notInService', and we
|
||||
proceed to interaction 4.
|
||||
|
||||
Interaction 4: Making the Conceptual Row Available
|
||||
|
||||
Once the management station is satisfied with the values
|
||||
associated with the columns of the conceptual row, it issues
|
||||
a management protocol set operation to set the status column
|
||||
to `active'. If the agent has sufficient information to
|
||||
make the conceptual row available for use by the managed
|
||||
device, the management protocol set operation succeeds (a
|
||||
`noError' response is returned). Otherwise, the management
|
||||
protocol set operation fails with an error of
|
||||
`inconsistentValue'.
|
||||
|
||||
|
||||
NOTE WELL
|
||||
|
||||
A conceptual row having a status column with value
|
||||
`notInService' or `notReady' is unavailable to the
|
||||
managed device. As such, it is possible for the
|
||||
managed device to create its own instances during the
|
||||
time between the management protocol set operation
|
||||
which sets the status column to `createAndWait' and the
|
||||
management protocol set operation which sets the status
|
||||
column to `active'. In this case, when the management
|
||||
protocol set operation is issued to set the status
|
||||
column to `active', the values held in the agent
|
||||
supersede those used by the managed device.
|
||||
|
||||
If the management station is prevented from setting the
|
||||
status column to `active' (e.g., due to management station
|
||||
or network failure) the conceptual row will be left in the
|
||||
`notInService' or `notReady' state, consuming resources
|
||||
indefinitely. The agent must detect conceptual rows that
|
||||
have been in either state for an abnormally long period of
|
||||
time and remove them. It is the responsibility of the
|
||||
DESCRIPTION clause of the status column to indicate what an
|
||||
abnormally long period of time would be. This period of
|
||||
time should be long enough to allow for human response time
|
||||
(including `think time') between the creation of the
|
||||
conceptual row and the setting of the status to `active'.
|
||||
In the absense of such information in the DESCRIPTION
|
||||
clause, it is suggested that this period be approximately 5
|
||||
minutes in length. This removal action applies not only to
|
||||
newly-created rows, but also to previously active rows which
|
||||
are set to, and left in, the notInService state for a
|
||||
prolonged period exceeding that which is considered normal
|
||||
for such a conceptual row.
|
||||
|
||||
|
||||
Conceptual Row Suspension
|
||||
|
||||
When a conceptual row is `active', the management station
|
||||
may issue a management protocol set operation which sets the
|
||||
instance of the status column to `notInService'. If the
|
||||
agent is unwilling to do so, the set operation fails with an
|
||||
error of `wrongValue'. Otherwise, the conceptual row is
|
||||
taken out of service, and a `noError' response is returned.
|
||||
It is the responsibility of the DESCRIPTION clause of the
|
||||
status column to indicate under what circumstances the
|
||||
status column should be taken out of service (e.g., in order
|
||||
for the value of some other column of the same conceptual
|
||||
row to be modified).
|
||||
|
||||
|
||||
Conceptual Row Deletion
|
||||
|
||||
For deletion of conceptual rows, a management protocol set
|
||||
operation is issued which sets the instance of the status
|
||||
column to `destroy'. This request may be made regardless of
|
||||
the current value of the status column (e.g., it is possible
|
||||
to delete conceptual rows which are either `notReady',
|
||||
`notInService' or `active'.) If the operation succeeds,
|
||||
then all instances associated with the conceptual row are
|
||||
immediately removed."
|
||||
SYNTAX INTEGER
|
||||
{
|
||||
active(1),
|
||||
notInService(2),
|
||||
notReady(3),
|
||||
createAndGo(4),
|
||||
createAndWait(5),
|
||||
destroy(6)
|
||||
}
|
||||
|
||||
-- the following two values are states:
|
||||
-- these values may be read or written
|
||||
-- the following value is a state:
|
||||
-- this value may be read, but not written
|
||||
-- the following three values are
|
||||
-- actions: these values may be written,
|
||||
-- but are never read
|
||||
TimeStamp ::= TEXTUAL-CONVENTION
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
"The value of the sysUpTime object at which a specific
|
||||
occurrence happened. The specific occurrence must be
|
||||
defined in the description of any object defined using this
|
||||
type."
|
||||
SYNTAX TimeTicks
|
||||
|
||||
|
||||
--
|
||||
-- Node definitions
|
||||
--
|
||||
|
||||
-- 1.3.6.1.4.1.1379
|
||||
cintelNS OBJECT IDENTIFIER ::= { enterprises 1379 }
|
||||
|
||||
|
||||
|
||||
END
|
||||
|
||||
--
|
||||
-- CINTEL-MIB.my
|
||||
--
|
||||
BIN
sshsvc/mibs/CINTEL-MIB.smidb
Normal file
BIN
sshsvc/mibs/CINTEL-MIB.smidb
Normal file
Binary file not shown.
26
sshsvc/setHLRServiceState
Normal file
26
sshsvc/setHLRServiceState
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
TargetIP="[2001:db8::9166]"
|
||||
PORT="34957"
|
||||
OID=".1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0"
|
||||
|
||||
case "$1" in
|
||||
1)
|
||||
echo -n "Set HLR state link down ... "
|
||||
snmpset -v3 -l noAuthNoPriv -u manager ${TargetIP}:${PORT} ${OID} i 1 >/dev/null
|
||||
echo "done"
|
||||
;;
|
||||
2)
|
||||
echo -n "Set HLR state link up ... "
|
||||
snmpset -v3 -l noAuthNoPriv -u manager ${TargetIP}:${PORT} ${OID} i 2 >/dev/null
|
||||
echo "done"
|
||||
;;
|
||||
3)
|
||||
echo -n "Set HLR state authentication failure ... "
|
||||
snmpset -v3 -l noAuthNoPriv -u manager ${TargetIP}:${PORT} ${OID} i 3 >/dev/null
|
||||
echo "done"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown state ($1)"
|
||||
;;
|
||||
esac
|
||||
553
sshsvc/snmp/snmp.go
Normal file
553
sshsvc/snmp/snmp.go
Normal file
@@ -0,0 +1,553 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
g "github.com/gosnmp/gosnmp"
|
||||
"github.com/slayercat/GoSNMPServer"
|
||||
"github.com/slayercat/GoSNMPServer/mibImps"
|
||||
)
|
||||
|
||||
type SNMPService struct {
|
||||
ListenAddr string
|
||||
ListenPort uint16
|
||||
UserName string
|
||||
AuthPass string
|
||||
AuthProto string
|
||||
PrivPass string
|
||||
PrivProto string
|
||||
EngineID string
|
||||
TrapPort uint16
|
||||
TrapListen bool
|
||||
TrapBool bool
|
||||
TrapTick uint16
|
||||
TimeOut uint16
|
||||
TrapTarget string
|
||||
|
||||
ListenHost string
|
||||
TrapHost string
|
||||
|
||||
SysName string
|
||||
SysDescr string
|
||||
SysLocation string
|
||||
SysContact string
|
||||
SysStatus string
|
||||
SysService int
|
||||
}
|
||||
|
||||
func (s *SNMPService) getAuthProto() g.SnmpV3AuthProtocol {
|
||||
switch s.AuthProto {
|
||||
case "NoAuth":
|
||||
return g.NoAuth
|
||||
case "MD5":
|
||||
return g.MD5
|
||||
case "SHA":
|
||||
return g.SHA
|
||||
default:
|
||||
}
|
||||
return g.MD5
|
||||
}
|
||||
|
||||
func (s *SNMPService) getPrivProto() g.SnmpV3PrivProtocol {
|
||||
switch s.PrivProto {
|
||||
case "NoPriv":
|
||||
return g.NoPriv
|
||||
case "DES":
|
||||
return g.DES
|
||||
case "AES":
|
||||
return g.AES
|
||||
case "AES192":
|
||||
return g.AES192
|
||||
case "AES256":
|
||||
return g.AES256
|
||||
default:
|
||||
}
|
||||
return g.DES
|
||||
}
|
||||
|
||||
func (s *SNMPService) setSecParamsList() []g.UsmSecurityParameters {
|
||||
var secParamsList = []g.UsmSecurityParameters{
|
||||
{
|
||||
UserName: s.UserName,
|
||||
AuthenticationProtocol: s.getAuthProto(),
|
||||
AuthenticationPassphrase: s.AuthPass,
|
||||
PrivacyProtocol: s.getPrivProto(),
|
||||
PrivacyPassphrase: s.PrivPass,
|
||||
AuthoritativeEngineID: s.EngineID,
|
||||
},
|
||||
// {
|
||||
// UserName: "myuser2",
|
||||
// AuthenticationProtocol: g.SHA,
|
||||
// AuthenticationPassphrase: "mypassword2",
|
||||
// PrivacyProtocol: g.DES,
|
||||
// PrivacyPassphrase: "myprivacy2",
|
||||
// AuthoritativeEngineID: s.EngineID,
|
||||
// },
|
||||
// {
|
||||
// UserName: "myuser2",
|
||||
// AuthenticationProtocol: g.MD5,
|
||||
// AuthenticationPassphrase: "mypassword2",
|
||||
// PrivacyProtocol: g.AES,
|
||||
// PrivacyPassphrase: "myprivacy2",
|
||||
// AuthoritativeEngineID: s.EngineID,
|
||||
// },
|
||||
}
|
||||
return secParamsList
|
||||
}
|
||||
|
||||
func (s *SNMPService) StartSNMPServer() {
|
||||
// 设置引擎启动次数和引varvar
|
||||
var engineBoots uint32 = 1
|
||||
//var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
|
||||
//var engineTime uint32 = 3600 // 使用当前时间初始化
|
||||
master := GoSNMPServer.MasterAgent{
|
||||
Logger: GoSNMPServer.NewDefaultLogger(),
|
||||
SecurityConfig: GoSNMPServer.SecurityConfig{
|
||||
NoSecurity: true,
|
||||
AuthoritativeEngineBoots: engineBoots,
|
||||
// OnGetAuthoritativeEngineTime: func() uint32 {
|
||||
// return engineTime
|
||||
// },
|
||||
//AuthoritativeEngineID: GoSNMPServer.SNMPEngineID{EngineIDData: "0x800007DB03360102101100"},
|
||||
|
||||
Users: s.setSecParamsList(),
|
||||
},
|
||||
SubAgents: []*GoSNMPServer.SubAgent{
|
||||
{
|
||||
UserErrorMarkPacket: false,
|
||||
CommunityIDs: []string{"public", "private"}, // SNMPv1 and SNMPv2c community strings
|
||||
OIDs: s.handleOIDs(),
|
||||
//OIDs: mibImps.All(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
server := GoSNMPServer.NewSNMPServer(master)
|
||||
err := server.ListenUDP("udp", s.ListenHost)
|
||||
if err != nil {
|
||||
log.Fatalf("Error in listen: %+v", err)
|
||||
}
|
||||
server.ServeForever()
|
||||
}
|
||||
|
||||
func (s *SNMPService) handleOIDs() []*GoSNMPServer.PDUValueControlItem {
|
||||
customOIDs := []*GoSNMPServer.PDUValueControlItem{
|
||||
{
|
||||
OID: "1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0",
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysName, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
// 将[]uint8转换为string
|
||||
if v, ok := value.([]uint8); ok {
|
||||
s.SysName = string(v)
|
||||
log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0 with value %v", s.SysName)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0",
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysStatus, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
// 将[]uint8转换为string
|
||||
if v, ok := value.([]uint8); ok {
|
||||
s.SysStatus = string(v)
|
||||
log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0 with value %v", s.SysStatus)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0",
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysDescr, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
// 将[]uint8转换为string
|
||||
if v, ok := value.([]uint8); ok {
|
||||
s.SysDescr = string(v)
|
||||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0 with value %v", s.SysDescr)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0",
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysLocation, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
// 将[]uint8转换为string
|
||||
if v, ok := value.([]uint8); ok {
|
||||
s.SysLocation = string(v)
|
||||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0 with value %v", s.SysLocation)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0",
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysContact, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
// 将[]uint8转换为string
|
||||
if v, ok := value.([]uint8); ok {
|
||||
s.SysContact = string(v)
|
||||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0 with value %v", s.SysContact)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.7.0",
|
||||
Type: g.TimeTicks,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return uint32(time.Now().Unix()), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0",
|
||||
Type: g.Integer,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
return s.SysService, nil
|
||||
},
|
||||
OnSet: func(value interface{}) error {
|
||||
if v, ok := value.(int); ok {
|
||||
s.SysService = v
|
||||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0 with value %v", s.SysService)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
// 为 GETBULK 新增处理 OIDs
|
||||
bulkOIDs := &GoSNMPServer.PDUValueControlItem{
|
||||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1", // 这里是您想要支持 GETBULK 的 OID 前缀
|
||||
Type: g.OctetString,
|
||||
OnGet: func() (value interface{}, err error) {
|
||||
// 假设我们返回一百度值,您可以根据您的实现进行调整
|
||||
values := []interface{}{s.SysName, s.SysStatus, s.SysDescr, s.SysLocation, s.SysContact, uint32(time.Now().Unix()), s.SysService} // 可以从其他结构中获取真实值
|
||||
return values, nil
|
||||
},
|
||||
}
|
||||
|
||||
customOIDs = append(customOIDs, bulkOIDs)
|
||||
|
||||
// 获取mibImps.All()返回的OID列表
|
||||
mibOIDs := mibImps.All()
|
||||
|
||||
// 使用Map来检测并移除重复的OID
|
||||
oidMap := make(map[string]*GoSNMPServer.PDUValueControlItem)
|
||||
for _, oid := range customOIDs {
|
||||
oidMap[oid.OID] = oid
|
||||
}
|
||||
for _, oid := range mibOIDs {
|
||||
if _, exists := oidMap[oid.OID]; !exists {
|
||||
oidMap[oid.OID] = oid
|
||||
} else {
|
||||
log.Printf("Duplicate OID found: %s", oid.OID)
|
||||
}
|
||||
}
|
||||
|
||||
// 将Map转换为Slice
|
||||
allOIDs := make([]*GoSNMPServer.PDUValueControlItem, 0, len(oidMap))
|
||||
for _, oid := range oidMap {
|
||||
allOIDs = append(allOIDs, oid)
|
||||
}
|
||||
|
||||
return allOIDs
|
||||
}
|
||||
|
||||
func (s *SNMPService) StartTrapServer() {
|
||||
flag.Usage = func() {
|
||||
fmt.Printf("Usage:\n")
|
||||
fmt.Printf(" %s\n", filepath.Base(os.Args[0]))
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
tl := g.NewTrapListener()
|
||||
tl.OnNewTrap = s.MyTrapHandler
|
||||
|
||||
usmTable := g.NewSnmpV3SecurityParametersTable(g.NewLogger(log.New(os.Stdout, "", 0)))
|
||||
for i := range s.setSecParamsList() {
|
||||
sp := &s.setSecParamsList()[i] // 使用指针
|
||||
err := usmTable.Add(sp.UserName, sp)
|
||||
if err != nil {
|
||||
usmTable.Logger.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置引擎启动次数和引varvar
|
||||
//var engineBoots uint32 = 1
|
||||
// var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
|
||||
//var engineTime uint32 = 3600 // 使用当前时间初始化
|
||||
gs := &g.GoSNMP{
|
||||
Target: s.TrapTarget,
|
||||
Port: s.TrapPort,
|
||||
Transport: "udp",
|
||||
Timeout: time.Duration(s.TimeOut) * time.Second, // 设置超时时间为x秒
|
||||
Version: g.Version3, // Always using version3 for traps, only option that works with all SNMP versions simultaneously
|
||||
MsgFlags: g.NoAuthNoPriv,
|
||||
SecurityModel: g.UserSecurityModel,
|
||||
SecurityParameters: &g.UsmSecurityParameters{
|
||||
UserName: s.UserName,
|
||||
AuthoritativeEngineID: s.EngineID,
|
||||
AuthoritativeEngineBoots: 1,
|
||||
//AuthoritativeEngineTime: 3600,
|
||||
AuthenticationProtocol: s.getAuthProto(),
|
||||
AuthenticationPassphrase: s.AuthPass,
|
||||
PrivacyProtocol: s.getPrivProto(),
|
||||
PrivacyPassphrase: s.PrivPass,
|
||||
},
|
||||
//TrapSecurityParametersTable: usmTable,
|
||||
ContextEngineID: s.EngineID,
|
||||
ContextName: "v3test",
|
||||
}
|
||||
tl.Params = gs
|
||||
tl.Params.Logger = g.NewLogger(log.New(os.Stdout, "", 0))
|
||||
|
||||
// 定时发送Trap
|
||||
if s.TrapBool {
|
||||
go s.SendPeriodicTraps(gs)
|
||||
}
|
||||
go s.monitorNetwork(gs)
|
||||
|
||||
if s.TrapListen {
|
||||
err := tl.Listen(s.TrapHost)
|
||||
if err != nil {
|
||||
log.Panicf("error in listen: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SNMPService) MyTrapHandler(packet *g.SnmpPacket, addr *net.UDPAddr) {
|
||||
log.Printf("got trapdata from %s\n", addr.IP)
|
||||
for _, v := range packet.Variables {
|
||||
switch v.Type {
|
||||
case g.OctetString:
|
||||
b := v.Value.([]byte)
|
||||
fmt.Printf("OID: %s, string: %x\n", v.Name, b)
|
||||
|
||||
default:
|
||||
log.Printf("trap: %+v\n", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SNMPService) SendPeriodicTraps(gs *g.GoSNMP) {
|
||||
err := gs.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Connect() err: %v", err)
|
||||
}
|
||||
defer gs.Conn.Close()
|
||||
|
||||
ticker := time.NewTicker(time.Duration(s.TrapTick) * time.Second) // 每10秒发送一次Trap
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C { // 每x秒发送一次Trap
|
||||
trap := g.SnmpTrap{
|
||||
Variables: []g.SnmpPDU{
|
||||
{
|
||||
Name: ".1.3.6.1.2.1.1.3.0",
|
||||
Type: g.TimeTicks,
|
||||
Value: uint32(time.Now().Unix()),
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.6.3.1.1.4.1.0",
|
||||
Type: g.ObjectIdentifier,
|
||||
Value: ".1.3.6.1.6.3.1.1.5.1",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = gs.SendTrap(trap)
|
||||
if err != nil {
|
||||
log.Printf("error sending trap: %s", err)
|
||||
} else {
|
||||
log.Printf("trap sent successfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 设备链路连接失败时发送Trap (LinkDown)
|
||||
func (s *SNMPService) sendLinkDownTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
|
||||
err := gs.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Connect() err: %v", err)
|
||||
}
|
||||
defer gs.Conn.Close()
|
||||
|
||||
trap := g.SnmpTrap{
|
||||
Variables: []g.SnmpPDU{
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1", // linkDown
|
||||
Type: g.OctetString,
|
||||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.1",
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1.1", // ifIndex
|
||||
Type: g.Integer,
|
||||
Value: ifIndex,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1.2", // ifDescr
|
||||
Type: g.OctetString,
|
||||
Value: ifDescr,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||||
Type: g.Integer,
|
||||
Value: 2, // event
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = gs.SendTrap(trap)
|
||||
if err != nil {
|
||||
log.Printf("error sending LinkDown trap: %s", err)
|
||||
} else {
|
||||
log.Printf("LinkDown trap sent successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 设备链路恢复正常时发送Trap (LinkUp)
|
||||
func (s *SNMPService) sendLinkUpTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
|
||||
err := gs.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Connect() err: %v", err)
|
||||
}
|
||||
defer gs.Conn.Close()
|
||||
|
||||
trap := g.SnmpTrap{
|
||||
Variables: []g.SnmpPDU{
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2", // linkUp
|
||||
Type: g.OctetString,
|
||||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.2",
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2.1", // ifIndex
|
||||
Type: g.Integer,
|
||||
Value: ifIndex,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2.2", // ifDescr
|
||||
Type: g.OctetString,
|
||||
Value: ifDescr,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||||
Type: g.Integer,
|
||||
Value: 5, // event
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = gs.SendTrap(trap)
|
||||
if err != nil {
|
||||
log.Printf("error sending LinkUp trap: %s", err)
|
||||
} else {
|
||||
log.Printf("LinkUp trap sent successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 设备鉴权失败时发送Trap (AuthenticationFailure)
|
||||
func (s *SNMPService) sendAuthFailureTrap(gs *g.GoSNMP, username, descr string) {
|
||||
err := gs.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Connect() err: %v", err)
|
||||
}
|
||||
defer gs.Conn.Close()
|
||||
|
||||
trap := g.SnmpTrap{
|
||||
Variables: []g.SnmpPDU{
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3", // authenticationFailure
|
||||
Type: g.OctetString,
|
||||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.3",
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3.1", // 自定义OID,用于记录失败的用户名
|
||||
Type: g.OctetString,
|
||||
Value: username,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3.2", // 自定义OID,用于记录描述
|
||||
Type: g.OctetString,
|
||||
Value: descr,
|
||||
},
|
||||
{
|
||||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||||
Type: g.Integer,
|
||||
Value: 4, // event
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = gs.SendTrap(trap)
|
||||
if err != nil {
|
||||
log.Printf("error sending AuthenticationFailure trap: %s", err)
|
||||
} else {
|
||||
log.Printf("AuthenticationFailure trap sent successfully")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SNMPService) monitorNetwork(gs *g.GoSNMP) {
|
||||
// 假设有一个函数 checkLinkStatus 返回链路状态
|
||||
for {
|
||||
serviceStatus := s.checkServiceStatus()
|
||||
switch strings.ToUpper(serviceStatus) {
|
||||
case "LINK_DOWN":
|
||||
index := 1
|
||||
ifDescr := fmt.Sprintf("Link(index=%d) DOWN", index)
|
||||
s.sendLinkDownTrap(gs, index, ifDescr) // 假设接口索引为1
|
||||
s.SysService = 0
|
||||
case "LINK_UP":
|
||||
index := 1
|
||||
ifDescr := fmt.Sprintf("Link(index=%d) UP", index)
|
||||
s.sendLinkUpTrap(gs, index, ifDescr) // 假设接口索引为1
|
||||
s.SysService = 0
|
||||
case "AUTH_FAILURE":
|
||||
descr := "Authentication Failure"
|
||||
s.sendAuthFailureTrap(gs, s.UserName, descr)
|
||||
s.SysService = 0
|
||||
default:
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Second) // 每10秒检查一次
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SNMPService) checkServiceStatus() string {
|
||||
switch s.SysService {
|
||||
case 1:
|
||||
return "LINK_DOWN"
|
||||
case 2:
|
||||
return "LINK_UP"
|
||||
case 3:
|
||||
return "AUTH_FAILURE"
|
||||
default:
|
||||
}
|
||||
return "NORMAL"
|
||||
}
|
||||
318
sshsvc/sshsvc.go
318
sshsvc/sshsvc.go
@@ -1,13 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"be.ems/lib/dborm"
|
||||
"be.ems/lib/global"
|
||||
@@ -15,15 +18,23 @@ import (
|
||||
"be.ems/lib/mmlp"
|
||||
"be.ems/sshsvc/config"
|
||||
"be.ems/sshsvc/logmml"
|
||||
"be.ems/sshsvc/snmp"
|
||||
omctelnet "be.ems/sshsvc/telnet"
|
||||
|
||||
//"github.com/gliderlabs/ssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var connNum int = 0
|
||||
var conf *config.YamlConfig
|
||||
|
||||
var (
|
||||
telnetCC int
|
||||
sshCC int
|
||||
telnetMu sync.Mutex
|
||||
sshMu sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
conf = config.GetYamlConfig()
|
||||
log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:sshsvc", config.GetLogLevel())
|
||||
@@ -57,33 +68,32 @@ func main() {
|
||||
serverConfig := &ssh.ServerConfig{
|
||||
PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
|
||||
// 这里可以进行密码验证逻辑,例如检查用户名和密码是否匹配
|
||||
validUser, _, err := dborm.XormCheckLoginUser(conn.User(), string(password), conf.OMC.UserCrypt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if validUser == true {
|
||||
sessionToken := fmt.Sprintf("%x", conn.SessionID()) // Generate new token to session ID
|
||||
sourceAddr := conn.RemoteAddr().String()
|
||||
timeOut := uint32(conf.Sshd.Timeout)
|
||||
sessionMode := conf.Sshd.Session
|
||||
log.Debugf("token:%s sourceAddr:%s", sessionToken, sourceAddr)
|
||||
affected, err := dborm.XormInsertSession(conn.User(), sourceAddr, sessionToken, timeOut, sessionMode)
|
||||
if err != nil {
|
||||
log.Error("Failed to insert Session table:", err)
|
||||
return nil, err
|
||||
}
|
||||
if affected == -1 {
|
||||
err := errors.New("Failed to get session")
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
// validUser, _, err := dborm.XormCheckLoginUser(conn.User(), string(password), conf.OMC.UserCrypt)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if validUser == true {
|
||||
// sessionToken := fmt.Sprintf("%x", conn.SessionID()) // Generate new token to session ID
|
||||
// sourceAddr := conn.RemoteAddr().String()
|
||||
// timeOut := uint32(conf.Sshd.Timeout)
|
||||
// sessionMode := conf.Sshd.Session
|
||||
// log.Debugf("token:%s sourceAddr:%s", sessionToken, sourceAddr)
|
||||
// affected, err := dborm.XormInsertSession(conn.User(), sourceAddr, sessionToken, timeOut, sessionMode)
|
||||
// if err != nil {
|
||||
// log.Error("Failed to insert Session table:", err)
|
||||
// return nil, err
|
||||
// }
|
||||
// if affected == -1 {
|
||||
// err := errors.New("Failed to get session")
|
||||
// log.Error(err)
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// if conn.User() == "admin" && string(password) == "123456" {
|
||||
// return nil, nil
|
||||
// }
|
||||
if handleAuth(conf.Sshd.AuthType, conn.User(), string(password)) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid user or password")
|
||||
},
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
@@ -101,8 +111,62 @@ func main() {
|
||||
log.Fatal("Failed to Listen: ", err)
|
||||
os.Exit(4)
|
||||
}
|
||||
//fmt.Printf("MML SSH server startup, listen port:%d\n", conf.Sshd.ListenPort)
|
||||
// 启动telnet服务器
|
||||
//telnetUri := fmt.Sprintf("%s:%d", conf.TelnetServer.ListenAddr, conf.TelnetServer.ListenPort)
|
||||
// telnetListener, err := net.Listen("tcp", telnetUri)
|
||||
// if err != nil {
|
||||
// log.Fatal("Failed to Listen: ", err)
|
||||
// os.Exit(4)
|
||||
// }
|
||||
//fmt.Printf("MML Telnet server startup, listen port:%d\n", conf.TelnetServer.ListenPort)
|
||||
// telnetconn, err := telnetListener.Accept()
|
||||
// if err != nil {
|
||||
// log.Fatal("Failed to accept telnet connection: ", err)
|
||||
// os.Exit(6)
|
||||
// }
|
||||
|
||||
fmt.Printf("MML SSH server startup, listen port:%d\n", conf.Sshd.ListenPort)
|
||||
telnetSvc := omctelnet.TelnetHandler{
|
||||
ListenAddr: conf.TelnetServer.ListenAddr,
|
||||
ListenPort: conf.TelnetServer.ListenPort,
|
||||
UserName: conf.TelnetServer.UserName,
|
||||
Password: conf.TelnetServer.Password,
|
||||
AuthType: conf.TelnetServer.AuthType,
|
||||
MaxConnNum: conf.TelnetServer.MaxConnNum,
|
||||
TagNE: conf.TelnetServer.TagNE,
|
||||
ListenHost: conf.TelnetServer.ListenAddr + ":" + strconv.Itoa(int(conf.TelnetServer.ListenPort)),
|
||||
}
|
||||
go telnetSvc.StartTelnetServer()
|
||||
// go StartTelnetServer(telnetSvc.ListenHost)
|
||||
|
||||
snmpSvc := snmp.SNMPService{
|
||||
ListenAddr: conf.SNMPServer.ListenAddr,
|
||||
ListenPort: conf.SNMPServer.ListenPort,
|
||||
UserName: conf.SNMPServer.UserName,
|
||||
AuthPass: conf.SNMPServer.AuthPass,
|
||||
AuthProto: conf.SNMPServer.AuthProto,
|
||||
PrivPass: conf.SNMPServer.PrivPass,
|
||||
PrivProto: conf.SNMPServer.PrivProto,
|
||||
EngineID: conf.SNMPServer.EngineID,
|
||||
TrapPort: conf.SNMPServer.TrapPort,
|
||||
TrapListen: conf.SNMPServer.TrapListen,
|
||||
TrapBool: conf.SNMPServer.TrapBool,
|
||||
TrapTick: conf.SNMPServer.TrapTick,
|
||||
TimeOut: conf.SNMPServer.TimeOut,
|
||||
TrapTarget: conf.SNMPServer.TrapTarget,
|
||||
|
||||
ListenHost: conf.SNMPServer.ListenAddr + ":" + strconv.Itoa(int(conf.SNMPServer.ListenPort)),
|
||||
TrapHost: conf.SNMPServer.ListenAddr + ":" + strconv.Itoa(int(conf.SNMPServer.TrapPort)),
|
||||
SysName: "HLR-0",
|
||||
SysStatus: "Normal",
|
||||
SysDescr: "HLR server(sysNO=0)",
|
||||
SysLocation: "Shanghai",
|
||||
SysContact: "",
|
||||
SysService: 0,
|
||||
}
|
||||
|
||||
go snmpSvc.StartSNMPServer()
|
||||
go snmpSvc.StartTrapServer()
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
@@ -115,6 +179,175 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func handleAuth(authType, userName, password string) bool {
|
||||
switch authType {
|
||||
case "local":
|
||||
if userName == conf.Sshd.UserName && password == conf.Sshd.Password {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case "radius":
|
||||
exist, err := dborm.XEngDB().Table("OMC_PUB.sysUser").Where("userName=? AND password=md5(?)", userName, password).Exist()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exist
|
||||
case "omc":
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func StartTelnetServer(addr string) {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting Telnet server:", err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
fmt.Println("Telnet server started on", addr)
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting Telnet connection:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
telnetMu.Lock()
|
||||
if telnetCC >= int(conf.TelnetServer.MaxConnNum) {
|
||||
telnetMu.Unlock()
|
||||
io.WriteString(conn, "Connection limit reached. Try again later.\r\n")
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
telnetCC++
|
||||
telnetMu.Unlock()
|
||||
|
||||
go handleTelnetConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleTelnetConnection(conn net.Conn) {
|
||||
defer func() {
|
||||
telnetMu.Lock()
|
||||
telnetCC--
|
||||
telnetMu.Unlock()
|
||||
}()
|
||||
defer conn.Close()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
writer := bufio.NewWriter(conn)
|
||||
|
||||
// 发送欢迎信息
|
||||
writer.WriteString("Welcome to the Telnet server!\r\n")
|
||||
writer.Flush()
|
||||
|
||||
// 请求用户名
|
||||
writer.WriteString("Username: ")
|
||||
writer.Flush()
|
||||
user, _ := reader.ReadString('\n')
|
||||
user = strings.TrimSpace(user)
|
||||
|
||||
// 关闭回显模式
|
||||
writer.Write([]byte{255, 251, 1}) // IAC WILL ECHO
|
||||
writer.Flush()
|
||||
|
||||
// 请求密码
|
||||
writer.WriteString("Password: ")
|
||||
writer.Flush()
|
||||
|
||||
// 读取密码并清除控制序列
|
||||
var passBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == 255 { // IAC
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
} else {
|
||||
passBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
pass := passBuilder.String()
|
||||
|
||||
// 恢复回显模式
|
||||
writer.Write([]byte{255, 252, 1}) // IAC WONT ECHO
|
||||
writer.Flush()
|
||||
|
||||
if handleAuth(conf.TelnetServer.AuthType, user, pass) {
|
||||
writer.WriteString("\r\nAuthentication successful!\r\n")
|
||||
writer.Flush()
|
||||
HandleCommands(user, conf.TelnetServer.TagNE, reader, writer)
|
||||
} else {
|
||||
writer.WriteString("\r\nAuthentication failed!\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命令输
|
||||
func HandleCommands(user, tag string, reader *bufio.Reader, writer *bufio.Writer) {
|
||||
header := fmt.Sprintf("[%s@%s]> ", user, tag)
|
||||
clearLine := "\033[2K\r" // ANSI 转义序列,用于清除当前行
|
||||
for {
|
||||
var commandBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == '\xff' || b == '\xfe' || b == '\x01' {
|
||||
continue
|
||||
}
|
||||
if b == 127 { // 处理退格键
|
||||
if commandBuilder.Len() > 0 {
|
||||
// 手动截断字符串
|
||||
command := commandBuilder.String()
|
||||
command = command[:len(command)-1]
|
||||
commandBuilder.Reset()
|
||||
commandBuilder.WriteString(command)
|
||||
writer.WriteString("\b \b") // 回显退格
|
||||
writer.Flush()
|
||||
}
|
||||
} else {
|
||||
// 回显用户输入的字符
|
||||
writer.WriteByte(b)
|
||||
writer.Flush()
|
||||
commandBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
command := strings.TrimSpace(commandBuilder.String())
|
||||
|
||||
// 处理其他命令
|
||||
switch command {
|
||||
case "hello":
|
||||
writer.WriteString("\r\nHello, world!\r\n")
|
||||
case "time":
|
||||
writer.WriteString(fmt.Sprintf("\r\nCurrent time: %s\r\n", time.Now().Format(time.RFC1123)))
|
||||
case "exit", "quit":
|
||||
writer.WriteString("\r\nGoodbye!\r\n")
|
||||
writer.Flush()
|
||||
return
|
||||
case "":
|
||||
default:
|
||||
writer.WriteString("\r\nUnknown command\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
writer.WriteString(clearLine + header)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func handleSSHConnection(conn net.Conn, serverConfig *ssh.ServerConfig) {
|
||||
// SSH握手
|
||||
sshConn, chans, reqs, err := ssh.NewServerConn(conn, serverConfig)
|
||||
@@ -141,13 +374,16 @@ func handleSSHConnection(conn net.Conn, serverConfig *ssh.ServerConfig) {
|
||||
continue
|
||||
}
|
||||
|
||||
connNum++
|
||||
if connNum > int(conf.Sshd.MaxConnNum) {
|
||||
sshMu.Lock()
|
||||
sshCC++
|
||||
if sshCC > int(conf.Sshd.MaxConnNum) {
|
||||
sshMu.Unlock()
|
||||
log.Error("Maximum number of connections exceeded")
|
||||
//conn.Write([]byte("Reach max connections"))
|
||||
channel.Write([]byte(fmt.Sprintf("Connection limit reached (limit=%d). Try again later.\r\n", conf.Sshd.MaxConnNum)))
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
sshMu.Unlock()
|
||||
|
||||
go handleSSHChannel(conn, sshConn, channel, requests)
|
||||
}
|
||||
@@ -196,8 +432,10 @@ func handleSSHChannel(conn net.Conn, sshConn *ssh.ServerConn, channel ssh.Channe
|
||||
}
|
||||
|
||||
func closeConnection(conn net.Conn) {
|
||||
sshMu.Lock()
|
||||
conn.Close()
|
||||
connNum--
|
||||
sshCC--
|
||||
sshMu.Unlock()
|
||||
}
|
||||
|
||||
func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
@@ -205,17 +443,22 @@ func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
// 检查通道是否支持终端
|
||||
|
||||
omcMmlVar := &mmlp.MmlVar{
|
||||
Version: "16.1.1",
|
||||
Version: global.Version,
|
||||
Output: mmlp.DefaultFormatType,
|
||||
MmlHome: conf.Sshd.MmlHome,
|
||||
Limit: 50,
|
||||
Limit: conf.Sshd.MaxConnNum,
|
||||
User: sshConn.User(),
|
||||
SessionToken: fmt.Sprintf("%x", sshConn.SessionID()),
|
||||
HttpUri: conf.OMC.HttpUri,
|
||||
UserAgent: config.GetDefaultUserAgent(),
|
||||
TagNE: conf.Sshd.TagNE,
|
||||
}
|
||||
term := term.NewTerminal(channel, fmt.Sprintf("[%s@%s]> ", omcMmlVar.User, omcMmlVar.TagNE))
|
||||
msg := fmt.Sprintf("\r\nWelcome to the %s server!\r\n", strings.ToUpper(omcMmlVar.TagNE))
|
||||
term.Write([]byte(msg))
|
||||
msg = fmt.Sprintf("Last login: %s from %s \r\n\r\n", time.Now().Format(time.RFC1123), sshConn.RemoteAddr())
|
||||
term.Write([]byte(msg))
|
||||
|
||||
term := term.NewTerminal(channel, fmt.Sprintf("[%s@omc]> ", omcMmlVar.User))
|
||||
// 启动交互式shell会话
|
||||
for {
|
||||
line, err := term.ReadLine()
|
||||
@@ -233,6 +476,13 @@ func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
}
|
||||
var response string
|
||||
switch cmdline {
|
||||
case "hello":
|
||||
term.Write([]byte("Hello, world!\r\n"))
|
||||
goto continueLoop
|
||||
case "time":
|
||||
response = fmt.Sprintf("Current time: %s\r\n", time.Now().Format(time.RFC1123))
|
||||
term.Write([]byte(response))
|
||||
goto continueLoop
|
||||
case "exit", "quit":
|
||||
goto exitLoop
|
||||
case "":
|
||||
|
||||
198
sshsvc/telnet/telnet.go
Normal file
198
sshsvc/telnet/telnet.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package omctelnet
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"be.ems/lib/dborm"
|
||||
)
|
||||
|
||||
type TelnetHandler struct {
|
||||
ListenAddr string
|
||||
ListenPort uint16
|
||||
UserName string
|
||||
Password string
|
||||
AuthType string
|
||||
MaxConnNum int
|
||||
TagNE string
|
||||
ListenHost string
|
||||
|
||||
connCount int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) handleTelnetAuth(authType, userName, password string) bool {
|
||||
switch authType {
|
||||
case "local":
|
||||
if userName == t.UserName && password == t.Password {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case "radius", "omc":
|
||||
exist, err := dborm.XEngDB().Table("OMC_PUB.sysUser").Where("userName=? AND password=md5(?)", userName, password).Exist()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exist
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) StartTelnetServer() {
|
||||
listener, err := net.Listen("tcp", t.ListenHost)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting Telnet server:", err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
fmt.Println("Telnet server started on", t.ListenHost)
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting Telnet connection:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
if t.connCount >= int(t.MaxConnNum) {
|
||||
t.mu.Unlock()
|
||||
msg := fmt.Sprintf("Connection limit reached (limit=%d). Try again later.\r\n", t.MaxConnNum)
|
||||
io.WriteString(conn, msg)
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
t.connCount++
|
||||
t.mu.Unlock()
|
||||
|
||||
go t.handleTelnetConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) handleTelnetConnection(conn net.Conn) {
|
||||
defer func() {
|
||||
t.mu.Lock()
|
||||
t.connCount--
|
||||
t.mu.Unlock()
|
||||
}()
|
||||
defer conn.Close()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
writer := bufio.NewWriter(conn)
|
||||
|
||||
// 发送欢迎信息
|
||||
|
||||
writer.WriteString(fmt.Sprintf("\r\nWelcome to the %s server!\r\n", strings.ToUpper(t.TagNE)))
|
||||
writer.Flush()
|
||||
|
||||
// 请求用户名
|
||||
writer.WriteString("Username: ")
|
||||
writer.Flush()
|
||||
user, _ := reader.ReadString('\n')
|
||||
user = strings.TrimSpace(user)
|
||||
|
||||
// 关闭回显模式
|
||||
writer.Write([]byte{255, 251, 1}) // IAC WILL ECHO
|
||||
writer.Flush()
|
||||
|
||||
// 请求密码
|
||||
writer.WriteString("Password: ")
|
||||
writer.Flush()
|
||||
|
||||
// 读取密码并清除控制序列
|
||||
var passBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == 255 { // IAC
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
} else {
|
||||
passBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
pass := passBuilder.String()
|
||||
|
||||
// 恢复回显模式
|
||||
writer.Write([]byte{255, 252, 1}) // IAC WONT ECHO
|
||||
writer.Flush()
|
||||
|
||||
if t.handleTelnetAuth(t.AuthType, user, pass) {
|
||||
msg := fmt.Sprintf("\r\n\r\nLast login: %s from %s \r\n\r\n", time.Now().Format(time.RFC1123), conn.RemoteAddr())
|
||||
writer.WriteString(msg)
|
||||
writer.Flush()
|
||||
t.HandleCommands(user, t.TagNE, reader, writer)
|
||||
} else {
|
||||
writer.WriteString("\r\nAuthentication failed!\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命令输
|
||||
func (t *TelnetHandler) HandleCommands(user, tag string, reader *bufio.Reader, writer *bufio.Writer) {
|
||||
header := fmt.Sprintf("[%s@%s]> ", user, tag)
|
||||
clearLine := "\033[2K\r" // ANSI 转义序列,用于清除当前行
|
||||
for {
|
||||
var commandBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == '\xff' || b == '\xfe' || b == '\x01' {
|
||||
continue
|
||||
}
|
||||
if b == 127 { // 处理退格键
|
||||
if commandBuilder.Len() > 0 {
|
||||
// 手动截断字符串
|
||||
command := commandBuilder.String()
|
||||
command = command[:len(command)-1]
|
||||
commandBuilder.Reset()
|
||||
commandBuilder.WriteString(command)
|
||||
writer.WriteString("\b \b") // 回显退格
|
||||
writer.Flush()
|
||||
}
|
||||
} else {
|
||||
// 回显用户输入的字符
|
||||
writer.WriteByte(b)
|
||||
writer.Flush()
|
||||
commandBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
command := strings.TrimSpace(commandBuilder.String())
|
||||
|
||||
// 处理其他命令
|
||||
switch command {
|
||||
case "hello":
|
||||
writer.WriteString("\r\nHello, world!\r\n")
|
||||
case "time":
|
||||
writer.WriteString(fmt.Sprintf("\r\nCurrent time: %s\r\n", time.Now().Format(time.RFC1123)))
|
||||
case "exit", "quit":
|
||||
writer.WriteString("\r\n\r\nGoodbye!\r\n")
|
||||
writer.Flush()
|
||||
return
|
||||
case "":
|
||||
default:
|
||||
writer.WriteString("\r\nUnknown command\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
writer.WriteString(clearLine + header)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user