Merit Network
Can't find what you're looking for? Search the Mail Archives.
  About Merit   Services   Network   Resources & Support   Network Research   News   Events   Home

Discussion Communities: Merit Network Email List Archives

Merit Joint Technical Staff

Date Prev | Date Next | Date Index | Thread Index | Author Index | Historical
Re: Specifications for the validation server

  • From: Richard S. Conto
  • Date: Fri Oct 08 12:54:12 1993

> originally from: David Jelinek <34AAQ77@CMUVM.CSV.CMICH.EDU>
> originally to:   mjts@merit.edu
> subject: Specifications for the validation server
> date: Fri, 08 Oct 1993 10:54:25 -0400
> ref: <931008.105425.EDT.34AAQ77@CMUVM.CSV.CMICH.EDU>
> --------
>Could someone please tell me where to find the specifications for the
>validation server? The C code would be nice too, but I really need the
>specifications as in descriptions of packets sent and received etc.

There complete source code for the Merit Authorization Server is
available via ANONYMOUS FTP from MERIT.EDU in "/pub/michnet/mnetauth.tar.Z".

In this compressed "tar" file is a file is a protocol description, in
"./doc/protocol.txt".

This server is going to be obsolete soon, because of the "NAS" work
(Network Access Server) is resulting in a "helper" protocol based on
Livingston's "radius" package, with enhancements.

I am enclosing this document.

--- Richard

--------------- "./doc/protocol.txt" -----------------------------
		The Merit Authorization Mechanism
			Protocol Description
						Created July 24, 1989
						by Richard S. Conto
 
  Change History:
   1989Jul24 (rsc) initial revision
   1989Jul28 (rsc) remove 'at_hlhost', add 'at_service', make
                   certain parameters required, make 'at_service'
                   a boolean (no requests), describe host locator
                   interaction with user.
   1990Sep05 (rsc) Update to current implimentation.
   1991Oct01 (rsc) at_opercmd, at_servicelist, and othher updates
		   current configuration.

 
  Protocol for Merit Network Authorization Service 
        (called from the Host Locator)
               Round 2.0
 
  Summary:
     Currently, an unreliable, unsequenced datagram protocol
  is the best transport service available for communicating
  with Internet attached hosts. The earlier protocol required
  a session with the authorizing (internet) host, which requires
  a reliable, sequenced record protocol (the protocol was text
  oriented, and the records were delimited by <eol>.) A re-design
  of the protocol to make it "stateless" would eliminate the need
  to design, build (and debug) a session layer on top of UDP, on
  both MTS and the intended servers (BSD Unix systems.)
     (There is a "Telnet" service available, but this would also
  require building a telnet protocol engine on top of a BSD
  "tcp" socket, and I wasn't able to quickly find sources for
  a telnet server suitable for this.)
 
 
 Proposal:
   1.) That the new protocol be a "binary" protocol. (Parsing
       a text protocol may be easy on MTS, but I'm not familiar
       enough with whatever Unix grammar compilers that exist
       that I feel confident about finishing this in a reasonable
       time.)
 
   2.) That each "packet" be entirely self contained. (i.e.:
       it will contain both ID and PW.) It will be reasonable
       to encrypt both ID and PW...
 
   3.) That the protocol be extensible for future needs.
 
   4.) That the protocol allow for an authorization server to
       act as an agent for another administration (i.e.: a Merit
       server acting to authorize for Ferris State.)
 
   5.) That the host locator prompt for an authorizer also
       prompt for an id, and include a way so that authorized, id,
       and password be supplied on one line, and that id and 
       password can be supplied when an id is prompted for so
       that users (and canned procedures) can quickly answer the
       request for authorization. (Also, a syntax for HL requests
       so that little prompting is required can be done as well.)
 
 
 
 *** Host Locator User Interface (what the user 'types') ***********
 
   There will be two different ways for a user (or program script)
 to pass information to the host locator, either by keyword, or
 by allowing the HL to prompt for further information. In the first
 case (keyword), the user (or more likely script) will be able
 to indicate at the "Which Host?" prompt (or %GRAB command) the
 following:
 
 >   Which Host? HOST=hostname AUTHCODE=auth-host/id PW=password
 >		 ***********************************************
 >
 > In this case, the host locator will NEVER prompt (even if
 > requested to by the 'auth-host'.) It will always respond with
 > the one-line message 'ALLOWED' or 'DENIED' (and if success, it
 > will pass on the connection.) If the host locator gets a prompt
 > request from the authorization server (auth-host), it will treat
 > it as 'DENIED'.
 
    In the other (prompt) case, the host locator may prompt as
 follows:
 
 >   Which Host? hostname
 >		 ********
 >   <service> authorization required. You must be authorized by
 >   a Merit certified host for access.
 >   Enter authorization code? auth-host/id [ [PW=]password ]
 >                   ****************************
 > Where <service> is the service the host locator needs to have
 > validated, such as "Internet" or "Surcharge". (This also means
 > that the host locator needs the concept of 'service' added to
 > it's algorithms and tables.)
 >
 > "auth-host/id" is the Merit "authorization code". It may be derivable
 > from the user's login id on the certified host "auth-host", but that is
 > not necessary. The password may be provided at this point. If not, Merit
 > will prompt for a password.
 >
 >   Password? password
 >             ********
 > A password must ALWAYS be supplied. Passwordless logins are not allowed to
 > be certified by a Merit certified host. Similarily, "anonymous" mechanisms
 > are not to be allowed.
 >
 > If more input is necessary, the protocol allows for prompting of further
 > input. Obviously, this won't be possible in the very first example above.
 >
 > If the host locator gets a "prompt" or "prompt_blanked"
 > request from the 'auth-host', it will use the prompt string
 > supplied with the request to as a prefix/prompt and wait for
 > input from the user.
 
   The Host Locator and the Authorizing Host (Authorization Server)
 will interact by sending data records to each other. In the case
 where MTS is the authorization server, the Host Locator will open
 a Merit/MTS server connection to MTS. All traffic will occur over
 this connection, until one end or the other closes the connection,
 or the Authorization Server sends a 'SUCCESS' or 'FAIL' status
 message (when the authorization server will disconnect.) In the
 case where an Internet attached host is the authorization server,
 the Host Locator will open a connection to the TLI host and send
 UDP packets to the internet host. It will initially time out after
 30 seconds, or upon the receipt of an ICMP message indicating that
 the network, host, or service is unavailable. (A 'service mapper'
 was described once by a RFC... it may be necessary to use this
 service mapper to determine what UDP port the authorization server
 is listening to.)  If the Host Locator receives a response, it will
 use 3 times the round trip time for further timeouts (if any
 further interaction is necessary.)
 
   Also, a number of fields are required from the host locator,
 such as ID, PW, TERMINFO, SERVICE and a few others. This will
 allow the Host Locator to pre-assemble a request record once 
 containing this information, and then add any reply that are
 prompted for. (The Host Locator must keep track of all replies,
 and replace any replies that are re-prompted for.)
 
   The authorization protocol I'm proposing will be structured as
 follows (I'll use "C"-like definition of the data structures.) All
 binary integers will be in "big-endian" form, which means (I
 believe) that the most significant byte "comes first."
 
   'authorize_t' has a 'sequence_id'. This id is used by the Host
 Locator to match it's requests to the Authorization Host with
 responses from the Authorization Host. Only the Host Locator may
 change this value (which makes it safe for 'little-endian' 
 machines). When the Authorization Server gets a request from the
 Host Locator, it must respond with the same 'sequence_id'. This
 will allow the Host Locator to throw away old, duplicate responses.
 (The Host Locator will quietly discard any responses that do not
 have the 'sequence_id' it is waiting for.)
 
 The following describes various types and data structures used
 in the transaction protocol (request and response.) This is not
 recommended as the internal representation of these fields within
 the Host Locator or Authorization Server (although they may be
 used if CPU cycles aren't a consideration ;-) ). This description
 attempts to describe things at 'octet' (or byte) boundaries.
 However, enumerated types and other data structures are used to
 illustrate how the data works, in a more human-understandable
 fashion.
 
 /*
  Type definitions and representations for the Merit Host Locator
  Authorization Server protocol. 
 
  Some of these types may be already declared in standard libraries.
  Use of those libraries is recommended. Their inclusion here is for
  illustrative purposes. These definitions have not yet been tested
  to see if they even compile, much less produce the correct
  data structures and offsets.
 
  ******************************************************************
 
   "int16" is a 2 byte integer. For some systems, this will be a
           normal 'int', for others it might be a 'short int'.
 
 */
 
 typedef int8 unsigned char;   /* range 0 to 255, in an octet. */
 
 typedef enum { at_ignore=0,	      at_id=1,		at_pw=2,
		at_billentity=3,      at_charges=4,	at_terminfo=5,
		at_mincharge=6,	      at_balance=7,	at_authid=8,
		at_message=9,	      at_reply=10,	at_prompt=11,
		at_prompt_blanked=12, at_cantreply=13,	at_status=14,
		at_service=15,	      at_dest=16,	at_binary=17,
		at_done=18,	      at_setpw=19,	at_userstatus=20,
		at_opercmd=21,	      at_servicelist=22
	 	} at_tag_t;
 
 typedef enum { at_status_denied=0, at_status_bad_id=1, at_status_bad_pw=2,
		at_status_bad_service=3, at_status_protocol_error=4,
		at_status_internal_error=5 } at_status_kind;

 typedef enum { false=0, true=1 } boolean;   /* Simple true/false */
 
 #define MAXSIZE 32767     /* Maximum size for MTS READ oper. */  \
                   -32     /* "authorize_t" overhead. */          \
                    -8     /* UDP overhead. */                    \
                   -24     /* minimum IP overhead. */ 
 
 struct
   authorize_t {
      int16 sequence_id,   /* Arbitrary identifier... */
      int8  version,       /* A version number, starting at 1. */
      int8  count,         /* Number of elements (below) */
      byte  elements[MAXSIZE] /* octet array of tagged items */
   };
 
 typedef struct {
    int8 len,              /* Length of string, 0 .. 255 */
    char text[255],        /* Text of string, if len > 0 */
   } string_t;          /* Like Plus's character(0 to 255) */
 
 typedef struct {
   int8 id,                /* For matching PROMPT with REPLY */
   string_t str            /* The prompt (and reply) */
  } promptstr_t;
 
 typedef struct {           /* What follows in element field. */
     int8 tag,              /* What follows... */
     union {
       string_t str,       /* for IGNORE, ID, PW, MESSAGE, AUTHID,
                              TERMINFO, CHARGES, BILLENTITY, DONE,
                              SERVICE, DEST, SETPW, USERSTATUS  */
       promptstr_t prompt, /* For PROMPT, PROMPT_BLANKED, BINARY, REPLY, and
				SERVICELIST */
       int16 num,          /* For MINCHARGE, BALANCE */
       int8  status,       /* For STATUS (failure indicator) (at_status_kind)*/
       u_char octet,       /* CANTREPLY is a boolean. */
       }
    } element_t; 
 
 The elements can occur in any order. Each element is "packed"
 to it's minimum size when it's addeded to the element array.
 (I.e.: strings only take up "len+1" bytes.) All string values
 are ASCII text, and are case insensitive... but the host locator
 probably shouldn't convert things to upper or lower case.
 
 Each "prompt" or "promptblanked" request must have a unique
 identification number assigned (i.e.: '1' for 'ID', '2' for
 'password', '3' for '*CKID ID', etc.) This identification is
 passed back on the 'reply' from the Host Locator to the
 Authorization Server, and is the only way to identify a particular
 response (since the 'prompt' and 'prompt_blanked' are lost.)
 
 Description of each tag:
 "at_ignore" (tag value 0): This is a string value that can be
     completely ignored. Treat it as a comment field.
     ... [ Bidirectional ] [ Optional ].
 
 "at_id" (tag value 1): This is the ENCRYPTED id being checked
     against. (The encryption mechanism has yet to be decided
     upon. Something based on the MTS CRYPT() should be used, since
     this can be de-crypted.
     ... [ HL -> AS ] [ Required ]
 
 "at_pw" (tag value 2): This is the ENCRYPTED pw (for the id)
     to check against. (See "at_id" for comments about encryption.)
     ... [ HL -> AS ] [ Required ]
 
 "at_billentity" (tag value 3): This indicates what "administration"
     handles authorization or billing. It should be present ONLY if
     the authorization server is different from the billentity.
     It allows one host to act as an authorizing agent for a group
     that doesn't have a host of their own. (i.e.: a Merit Sun
     acting as an authorizor for Central Michigan Univ, or Ferris
     State.) (The billentity is ALWAYS included on the 'network id'
     of a Host Locator approved connection.)
     ... [ HL -> AS ] [ Required if the 'billentity' is different
                        from the AS. ]
 
 "at_charges" (tag value 4): This is the Merit "Charging
     Entity", taken from the "CE=" field of the Merit Network
     Rate Record. Currently, this has the values: "Telenet",
     "Autonet", or "Merit". It indicates what organization or
     unit is generating the charges. It's presence REQUIRES
     that the Authorization Server respond with 'at_balance'.
     ... [ HL -> AS ] [ (obs) Required if present in Merit Rate Record. ]
 
 "at_terminfo" (tag value 5): This is the MTS ANSWERBACK(long).
     This allows some sort of independant tracing at the
     Authorization Server of password failures or illegal usage 
     without necessitating checking through logs on MTS.
     The one from MTS contains the MTS answerback prefixed with
     the text "MTS:". The test programs prefix it with "TEST:".
     The Hermes-Agent should prefix it with "HA(hostname):"
     ... [ HL -> AS ] [ Required ]
 
 "at_mincharge" (tag value 6): This is the minimum estimated 
     charge to be incurred for 1 minute of use of this service. 
     ... [ HL -> AS ] [ (obs) Required if 'at_charges' is sent ] 
 
 "at_balance" (tag value 7): This is the minimum balance available. 
     (Can be used by the HL to deny service, if the minimum 
     balance is too small.) 
     ... [ AS -> HL ] [ (obs) Required in response to request with 
         'at_chargeentity' ] 
 
 "at_authid" (tag value 8): This is a character string containing 
     PRINTABLE ascii characters, excluding <space> (" "), <comma> 
     (","), and <equals> ("=").  This token is used by the network
     to identify a user for audit trails. It is also presented back
     to the authorizing host when receipts and bills are produced for
     charged services.
     ... [ AS -> HL ] [ ONLY if the authorization succeeds. NO AT_STATUS ]
 
 "at_message" (tag value 9): A simple one-line string message 
     to be printed on the user's terminal by the HL. Multi-line 
     messages are printed by seperate "at_message" things, in 
     the order they occur. 
     ... [ AS -> HL ] [ Optional ] 
 
 "at_reply" (tag value 10): This is the response to an "at_prompt" 
     or "at_prompt_blanked". This is sent from the HL to the 
     Authorization Server. The "reply" contains an identification 
     number, that corresponds to the "prompt" or "prompt_blanked" 
     that caused it.  More than 1 reply to the same prompt is 
     explicitly disallowed. The Host Locator must keep track of 
     all replys, and must always transmit all known replies. 
     If prompted for an existing reply, it must replace that 
     reply with a new one obtained from the user. 
     ... [ HL -> AS ] [ All current replies must be included ] 
 
 "at_prompt" (tag value 11): This is a prompting string (1 
     line of text only). It is originally sent from the 
     Authorization Server to the HL if the authorization server 
     needs more information. An identification field on the 
     prompt request is used to match it with it's reply. 
     Only the identification number in the 'at_prompt' identifies 
     it when the reply is returned to the Authorization Server. 
     'at_prompt' is treated as 'at_status' == false (fail), if 
     'at_canreply' was sent on the HL -> AS request. (In fact, 
     'at_status' = false should be sent instead.) The text string 
     included with 'at_prompt' (and 'at_prompt_blanked' below) 
     is used as an prefix/prompt string for requesting more 
     input from the user. The order of prompts presented to the 
     user is the same as the left-to-right order (low offset to 
     high offset) as encountered in the 'elements' array in the 
     'authorize_t' structure. 'at_prompt' and 'at_prompt_blanked' 
     preclude the presence of 'at_status' (and vice-versa.) 
     'at_prompt' is the ONLY way for the Authorization Server 
     to get more information from the Host Locator. 
     ... [ AS -> HL ] [ Disallowed if 'at_cantreply', treated 
         as 'at_status' == false. ] [ Disallowed if 'at_status' 
         is present. ] 
 
 "at_prompt_blanked" (tag value 12): This is like "at_prompt", 
     except that any input typed by the user is not echoed, or 
     otherwise overprinted so that it can't be accidentally 
     revealed. 
     ... [ AS -> HL ] [ Disallowed if 'at_cantreply', treated 
         as 'at_status' == false. ] [ Disallowed if 'at_status' 
         is present. ] 
 
 "at_cantreply" (tag value 13): This indicates that the HL 
     will be unable to respond to an "at_prompt" or an 
     "at_prompt_blanked". It only occurs when the Host Locator 
     processes a keyword 'HOST=hostname AUTH=auth-host' ... 
     request. This is so that machine scripts can easily 
     determine success or failure and don't need to check for 
     id/pw replacement prompting from the Authorization Server 
     (through the Host Locator.) 
     ... [ HL -> AS ] [ Required ONLY for Keyword Requests. ] 
 
 "at_status" (tag value 14): If present, it indicates the reason for a
     failure. No further interactions with the Authorization Server will
     occur, and any connections may be closed. See "at_status_kind" above
     for some of the reasons. A generic "at_status_denied" may be presented,
     if that is the only information the server feels safe to present, but
     Merit would like to know about id and pw failures, to assist in tracking
     down problems.
     ... [ AS -> HL ] [ Disallowed if "at_prompt", "at_prompt_blanked", or
	  "at_authid" are present. ]
 
 "at_service" (tag value 15): This string value indicates 
     what service the Host Locator is trying to get authorization 
     for. (Initial values might be "Internet", "Telenet", 
     "Autonet", "Merit", "UM-Laserwriter", and "UM-Dialout".) 
     ... [ HL -> AS ] [ Required ] 
 
 "at_dest" (tag value 16): This is the "hostname" that 
     the host locator is trying to resolve (and gain access 
     to.) This name should be translated to uppercase. 
     ... [ HL -> AS ] [ Required ] [ Translated to uppercase ] 
 
  "at_binary" (tag value 17): This field allows the Host Locator to
     keep state information (such as password failure counts) 
     between exchanges with the authorization server. The authorization
     server is not required to use this, but if it may use it if the
     server trusts the Host Locator sufficiently.  The "at_binary"
     fields are themselves indexed, so that multiple state variables may
     be stored in the Host Locator. The Host Locator itself does not
     examine these values, but simply passes them back on subsequent
     transmissions to the server.
	[ AS -> HL, then HL -> AS ] [ HL is required to pass this back
	if presented by the AS.]

  "at_done" (tag value 18): This is like "at_cantreply" in that it
     indicates that the HL will not be interacting with the AS again. It
     requests that any running "daemon" process be terminated. (The daemon
     process may be restarted.) This is to help clear out any resources the
     authorization server may be using when they are no longer needed. This
     tag is only sent when configured in the HL. It passes a string indicating
     the "reason".
	[ HL -> AS ] [ Configurable ]

  "at_setpw" (tag value 19): This string offers a new password to the server.
     This is a reserved value, and isn't implimented yet.
	[ HL -> AS ] [ not implimented, reserved. ]

  "at_userstatus" (tag value 20): This allows the Merit NOC to "disable" an
     account for the purposes of "authorization". (I.e.: if a user reports to
     the NOC that someone has stolen their password.) The string values
     "enable", "disable", and "status" are required commands. An ID is
     required, but a password isn't.
	[ HL -> AS ] [ control function. ]

  "at_opercmd" (tag value 21): This allows the Merit NOC to issue operational
	commands to the authorization server. This function should make the
	at_userstatus command obsolete, since at_userstatus is insecure.
	[ HL -> AS ] [ control function. ]

  "at_servicelist" (tag value 22): This is a list of service and octet pairs.
	The client (the host locator) sends a list to be approved. The
	server responds with the approved list. This allows the client to
	determine which services of a list are allowed. The client may use the
	octet value for any purpose. (Using the octets as a bit mask to be
	logicaly 'or'ed together to determine a capability bit mask is 
	intended for this.)
	[ HL <-> AS ] 


 Examples:
  A Host Locator needs to determine access a Telenet user trying
  to gain access to "umlib". The Host Locator prompts for an
  authorizer/id/pw with, but only gets an authorizer and ID. It then
  sends the following request to the authorizer:
 
  authorize_t { '4242', '01', '06',  /* Seq=16962, Ver 1, 6 Items */
     {'01','03','6D7473' }, /* at_id = "mts" */
     {'04','07','54656C65 6E6574'}, /* at_chargeentity = "Telenet"*/
     {'05','31','41593233 2D583354 453B5445 40414E3B 54583032 3A4E4F4E
                 45202020 20333131 30333133 30303036 3220463D 30313031
                 '} /* at_terminfo =
         "AY23-X3TE;TE@AN;TX02:NONE    311031300062 F=0101" */
     {'06','0005' }       /* at_mincharge = 5 cents. */
     {'0F','03','54656C65 6E6574'}, /* at_service = "Telenet" */
     {'10','05','554D4C49 42'}   /* at_dest = "UMLIB" */
    };
 
 In the future, I'll show this as:
   authorize_t  { 16962, 1, 6,
    {at_id, "mts" },
    {at_chargeentity, "Telenet" } 
    {at_terminfo, "AY23-X3TE;TE@AN;TX02:NONE    311031300062 F=0101"}
    {at_mincharge, 5}
    {at_service,"Telenet"},
    {at_dest,"umlib"}
   }
 
 
  The Authorization server host responds, indicating that a pw is
 required (it responds to the initiating UDP port number, using the
 same 'sequence id' number on the 'authorize_t' record as sent by the
 HL.)
 
   authorize_t { 16962, 1, 1,
    {at_prompt_blanked, 1, "Password? "}
     }
 
  The Host locator then prompts the user for whatever the 
 authorization server requested. The HL doesn't really know what
 it's prompting for... just the index number and the prompt string.
 Note: We might want to use some reversible encryption here. The HL
 changes (increases) the sequence ID (so the responses can be 
 correctly identified),
 
   authorize_t  { 16963, 1, 7,
    {at_id, "mts"}
    {at_chargeentity, "Telenet" } 
    {at_terminfo, "AY23-X3TE;TE@AN;TX02:NONE    311031300062 F=0101"}
    {at_mincharge, 5}
    {at_service,"Telenet"}
    {at_dest,"umlib"}
    {at_reply, 1, "mastercard"}
   }
 
   If the id/pw combination were invalid, the server could return
 the following:
 
   authorize_t  { 16963, 1, 2,
    {at_message,"Invalid id or password"}
    {at_status, 0 }
   }
 
   Or, if the server wanted to re-try the pw, it could:
 
   authorize_t  { 16963, 1, 2,
    {at_message,"Invalid password, please try again."}
    {at_prompt_blanked, 1, "Password? "}
   }
 
   The Host Locator would discard the previous value of prompt #1,
 prompt again from the user (using the supplied prompt string), and
 send that result back... until prompted again for that same prompt
 index.
 
 
   Of course, if the id/pw combination were valid, the server would
 return:
 
   authorize_t  { 16963, 1, 3,
    {at_message, "You've got $101.56 ... go for it."}
    {at_balance=10156}  /* $101.56 */
    {at_token, "WMTSMTS."}
    {at_status, 1 }
   }
 
 ****************************************************************
 
 Example: 
   Here, the user supplied a password as well as an ID. The
 interaction would be simpler.
 
   authorize_t  { 16962, 1, 7,
    {at_id, "mts" },
    {at_pw, "mastercard"}
    {at_chargeentity, "Telenet" } 
    {at_terminfo, "AY23-X3TE;TE@AN;TX02:NONE    311031300062 F=0101"}
    {at_mincharge, 5}
    {at_service,"Telenet"},
    {at_dest,"umlib"}
   }
 
 
   If the id/pw combination were invalid, the server could return
 the following:
 
   authorize_t  { 16963, 1, 2,
    {at_message,"Invalid id or password"}
    {at_status, 0 }
   }
 
   Or, if the server wanted to re-try the pw, it could prompt:
 
   authorize_t  { 16963, 1, 2,
    {at_message,"Invalid password, please try again."}
    {at_prompt_blanked, 1, "Password? "}
   }
  
  Of course, the HL won't really know that it's prompting for a
  password. The authorization server will have to know that an
  explicit prompt #1 (for it's "Password? " prompt) overrides the
  at_pw field.
 
   authorize_t  { 16962, 1, 8,
    {at_id, "mts" },
    {at_pw, "mastercard"}
    {at_chargeentity, "Telenet" } 
    {at_terminfo, "AY23-X3TE;TE@AN;TX02:NONE    311031300062 F=0101"}
    {at_mincharge, 5}
    {at_service,"Telenet"},
    {at_dest,"umlib"}
    {at_reply, 1, "discovercredit"}
   }
 
 
- - - - - - - - - - - - - - - - -




Discussion Communities


About Merit | Services | Network | Resources & Support | Network Research
News | Events | Contact | Site Map | Merit Network Home


Merit Network, Inc.