[前][次][番号順一覧][スレッド一覧]

ruby-changes:33491

From: drbrain <ko1@a...>
Date: Sat, 12 Apr 2014 09:35:09 +0900 (JST)
Subject: [ruby-changes:33491] drbrain:r45572 (trunk): * ext/openssl/ossl_ocsp.c: [DOC] Document OpenSSL::OCSP.

drbrain	2014-04-12 09:35:01 +0900 (Sat, 12 Apr 2014)

  New Revision: 45572

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=45572

  Log:
    * ext/openssl/ossl_ocsp.c:  [DOC] Document OpenSSL::OCSP.

  Modified files:
    trunk/ChangeLog
    trunk/ext/openssl/ossl_ocsp.c
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 45571)
+++ ChangeLog	(revision 45572)
@@ -1,3 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sat Apr 12 09:26:48 2014  Eric Hodel  <drbrain@s...>
+
+	* ext/openssl/ossl_ocsp.c:  [DOC] Document OpenSSL::OCSP.
+
 Fri Apr 11 18:52:38 2014  Koichi Sasada  <ko1@a...>
 
 	* array.c (ARY_SET): added.
Index: ext/openssl/ossl_ocsp.c
===================================================================
--- ext/openssl/ossl_ocsp.c	(revision 45571)
+++ ext/openssl/ossl_ocsp.c	(revision 45572)
@@ -99,6 +99,15 @@ ossl_ocspreq_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L99
     return obj;
 }
 
+/*
+ * call-seq:
+ *   OpenSSL::OCSP::Request.new              -> request
+ *   OpenSSL::OCSP::Request.new(request_der) -> request
+ *
+ * Creates a new OpenSSL::OCSP::Request.  The request may be created empty or
+ * from a +request_der+ string.
+ */
+
 static VALUE
 ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self)
 {
@@ -121,6 +130,17 @@ ossl_ocspreq_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L130
     return self;
 }
 
+/*
+ * call-seq:
+ *   request.add_nonce(nonce = nil) -> request
+ *
+ * Adds a +nonce+ to the OCSP request.  If no nonce is given a random one will
+ * be generated.
+ *
+ * The nonce is used to prevent replay attacks but some servers do not support
+ * it.
+ */
+
 static VALUE
 ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self)
 {
@@ -143,18 +163,25 @@ ossl_ocspreq_add_nonce(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L163
     return self;
 }
 
-/* Check nonce validity in a request and response.
- * Return value reflects result:
- *  1: nonces present and equal.
- *  2: nonces both absent.
- *  3: nonce present in response only.
- *  0: nonces both present and not equal.
- *  -1: nonce in request only.
- *
- *  For most responders clients can check return > 0.
- *  If responder doesn't handle nonces return != 0 may be
- *  necessary. return == 0 is always an error.
+/*
+ * call-seq:
+ *   request.check_nonce(response) -> result
+ *
+ * Checks the nonce validity for this request and +response+.
+ *
+ * The return value is one of the following:
+ *
+ * -1 :: nonce in request only.
+ *  0 :: nonces both present and not equal.
+ *  1 :: nonces present and equal.
+ *  2 :: nonces both absent.
+ *  3 :: nonce present in response only.
+ *
+ * For most responses, clients can check +result+ > 0.  If a responder doesn't
+ * handle nonces <code>result.nonzero?</code> may be necessary.  A result of
+ * <code>0</code> is always an error.
  */
+
 static VALUE
 ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp)
 {
@@ -169,6 +196,13 @@ ossl_ocspreq_check_nonce(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L196
     return INT2NUM(res);
 }
 
+/*
+ * call-seq:
+ *   request.add_certid(certificate_id) -> request
+ *
+ * Adds +certificate_id+ to the request.  
+ */
+
 static VALUE
 ossl_ocspreq_add_certid(VALUE self, VALUE certid)
 {
@@ -183,6 +217,13 @@ ossl_ocspreq_add_certid(VALUE self, VALU https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L217
     return self;
 }
 
+/*
+ * call-seq:
+ *   request.certid -> [certificate_id, ...]
+ *
+ * Returns all certificate IDs in this request.
+ */
+
 static VALUE
 ossl_ocspreq_get_certid(VALUE self)
 {
@@ -206,6 +247,17 @@ ossl_ocspreq_get_certid(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L247
     return ary;
 }
 
+/*
+ * call-seq:
+ *   request.sign(signer_cert, signer_key)                      -> self
+ *   request.sign(signer_cert, signer_key, certificates)        -> self
+ *   request.sign(signer_cert, signer_key, certificates, flags) -> self
+ *
+ * Signs this OCSP request using +signer_cert+ and +signer_key+.
+ * +certificates+ is an optional Array of certificates that may be included in
+ * the request.
+ */
+
 static VALUE
 ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
 {
@@ -234,6 +286,14 @@ ossl_ocspreq_sign(int argc, VALUE *argv, https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L286
     return self;
 }
 
+/*
+ * call-seq:
+ *   request.verify(certificates, store)        -> true or false
+ *   request.verify(certificates, store, flags) -> true or false
+ *
+ * Verifies this request using the given +certificates+ and X509 +store+.
+ */
+
 static VALUE
 ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self)
 {
@@ -255,6 +315,10 @@ ossl_ocspreq_verify(int argc, VALUE *arg https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L315
     return result ? Qtrue : Qfalse;
 }
 
+/*
+ * Returns this request as a DER-encoded string
+ */
+
 static VALUE
 ossl_ocspreq_to_der(VALUE self)
 {
@@ -278,6 +342,13 @@ ossl_ocspreq_to_der(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L342
 /*
  * OCSP::Response
  */
+
+/* call-seq:
+ *   OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response
+ *
+ * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+.
+ */
+
 static VALUE
 ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp)
 {
@@ -308,6 +379,15 @@ ossl_ocspres_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L379
     return obj;
 }
 
+/*
+ * call-seq:
+ *   OpenSSL::OCSP::Response.new               -> response
+ *   OpenSSL::OCSP::Response.new(response_der) -> response
+ *
+ * Creates a new OpenSSL::OCSP::Response.  The response may be created empty or
+ * from a +response_der+ string.
+ */
+
 static VALUE
 ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self)
 {
@@ -330,6 +410,13 @@ ossl_ocspres_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L410
     return self;
 }
 
+/*
+ * call-seq:
+ *   response.status -> Integer
+ *
+ * Returns the status of the response.
+ */
+
 static VALUE
 ossl_ocspres_status(VALUE self)
 {
@@ -342,6 +429,13 @@ ossl_ocspres_status(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L429
     return INT2NUM(st);
 }
 
+/*
+ * call-seq:
+ *   response.status_string -> String
+ *
+ * Returns a status string for the response.
+ */
+
 static VALUE
 ossl_ocspres_status_string(VALUE self)
 {
@@ -354,6 +448,13 @@ ossl_ocspres_status_string(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L448
     return rb_str_new2(OCSP_response_status_str(st));
 }
 
+/*
+ * call-seq:
+ *   response.basic
+ *
+ * Returns a BasicResponse for this response
+ */
+
 static VALUE
 ossl_ocspres_get_basic(VALUE self)
 {
@@ -369,6 +470,13 @@ ossl_ocspres_get_basic(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L470
     return ret;
 }
 
+/*
+ * call-seq:
+ *   response.to_der -> String
+ *
+ * Returns this response as a DER-encoded string.
+ */
+
 static VALUE
 ossl_ocspres_to_der(VALUE self)
 {
@@ -405,12 +513,27 @@ ossl_ocspbres_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L513
     return obj;
 }
 
+/*
+ * call-seq:
+ *   OpenSSL::OCSP::BasicResponse.new(*) -> basic_response
+ *
+ * Creates a new BasicResponse and ignores all arguments.
+ */
+
 static VALUE
 ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self)
 {
     return self;
 }
 
+/*
+ * call-seq:
+ *   basic_response.copy_nonce(request) -> Integer
+ *
+ * Copies the nonce from +request+ into this response.  Returns 1 on success
+ * and 0 on failure.
+ */
+
 static VALUE
 ossl_ocspbres_copy_nonce(VALUE self, VALUE request)
 {
@@ -425,6 +548,14 @@ ossl_ocspbres_copy_nonce(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L548
     return INT2NUM(ret);
 }
 
+/*
+ * call-seq:
+ *   basic_response.add_nonce(nonce = nil)
+ *
+ * Adds +nonce+ to this response.  If no nonce was provided a random nonce
+ * will be added.
+ */
+
 static VALUE
 ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self)
 {
@@ -447,6 +578,22 @@ ossl_ocspbres_add_nonce(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L578
     return self;
 }
 
+/*
+ * call-seq:
+ *   basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response
+ *
+ * Adds a validation +status+ (0 for revoked, 1 for success) to this
+ * response for +certificate_id+.  +reason+ describes the reason for the
+ * revocation, if any.
+ *
+ * The +revocation_time+, +this_update+ and +next_update+ are times for the
+ * certificate's revocation time, the time of this status and the next update
+ * time for a new status, respectively.
+ *
+ * +extensions+ may be an Array of OpenSSL::X509::Extension that will
+ * be added to this response or nil.
+ */
+
 static VALUE
 ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status,
 			 VALUE reason, VALUE revtime,
@@ -515,6 +662,16 @@ ossl_ocspbres_add_status(VALUE self, VAL https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L662
     return self;
 }
 
+/*
+ * call-seq:
+ *   basic_response.status -> statuses
+ *
+ * Returns an Array of statuses for this response.  Each status contains a
+ * CertificateId, the status (0 for success, 1 for revoked), the reason for
+ * the status, the revocation time, the time of this update, the time for the
+ * next update and a list of OpenSSL::X509::Extensions.
+ */
+
 static VALUE
 ossl_ocspbres_get_status(VALUE self)
 {
@@ -560,6 +717,16 @@ ossl_ocspbres_get_status(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L717
     return ret;
 }
 
+/*
+ * call-seq:
+ *   basic_response.sign(signer_cert, signer_key) -> self
+ *   basic_response.sign(signer_cert, signer_key, certificates) -> self
+ *   basic_response.sign(signer_cert, signer_key, certificates, flags) -> self
+ *
+ * Signs this response using the +signer_cert+ and +signer_key+.  Additional
+ * +certificates+ may be added to the signature along with a set of +flags+.
+ */
+
 static VALUE
 ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
 {
@@ -590,6 +757,14 @@ ossl_ocspbres_sign(int argc, VALUE *argv https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L757
     return self;
 }
 
+/*
+ * call-seq:
+ *   basic_response.verify(certificates, store) -> true or false
+ *   basic_response.verify(certificates, store, flags) -> true or false
+ *
+ * Verifies the signature of the response using the given +certificates+,
+ * +store+ and +flags+.
+ */
 static VALUE
 ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
 {
@@ -627,6 +802,15 @@ ossl_ocspcid_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L802
     return obj;
 }
 
+/*
+ * call-seq:
+ *   OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id
+ *
+ * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and
+ * +issuer+ X509 certificates.  The +digest+ is used to compute the
+ * certificate ID and must be an OpenSSL::Digest instance.
+ */
+
 static VALUE
 ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
 {
@@ -657,6 +841,13 @@ ossl_ocspcid_initialize(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L841
     return self;
 }
 
+/*
+ * call-seq:
+ *   certificate_id.cmp(other) -> true or false
+ *
+ * Compares this certificate id with +other+ and returns true if they are the
+ * same.
+ */
 static VALUE
 ossl_ocspcid_cmp(VALUE self, VALUE other)
 {
@@ -670,6 +861,14 @@ ossl_ocspcid_cmp(VALUE self, VALUE other https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L861
     return (result == 0) ? Qtrue : Qfalse;
 }
 
+/*
+ * call-seq:
+ *   certificate_id.cmp_issuer(other) -> true or false
+ *
+ * Compares this certificate id's issuer with +other+ and returns true if
+ * they are the same.
+ */
+
 static VALUE
 ossl_ocspcid_cmp_issuer(VALUE self, VALUE other)
 {
@@ -683,6 +882,13 @@ ossl_ocspcid_cmp_issuer(VALUE self, VALU https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L882
     return (result == 0) ? Qtrue : Qfalse;
 }
 
+/*
+ * call-seq:
+ *   certificate_id.get_serial -> Integer
+ *
+ * Returns the serial number of the issuing certificate.
+ */
+
 static VALUE
 ossl_ocspcid_get_serial(VALUE self)
 {
@@ -696,10 +902,130 @@ ossl_ocspcid_get_serial(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L902
 void
 Init_ossl_ocsp()
 {
+    /*
+     * OpenSSL::OCSP implements Online Certificate Status Protocol requests
+     * and responses.
+     *
+     * Creating and sending an OCSP request requires a subject certificate
+     * that contains an OCSP URL in an authorityInfoAccess extension and the
+     * issuer certificate for the subject certificate.  First, load the issuer
+     * and subject certificates:
+     *
+     *   subject = OpenSSL::X509::Certificate.new subject_pem
+     *   issuer  = OpenSSL::X509::Certificate.new issuer_pem
+     *
+     * To create the request we need to create a certificate ID for the
+     * subject certificate so the CA knows which certificate we are asking
+     * about:
+     *
+     *   digest = OpenSSL::Digest::SHA1.new
+     *   certificate_id =
+     *     OpenSSL::OCSP::CertificateId.new subject, issuer, digest
+     *
+     * Then create a request and add the certificate ID to it:
+     *
+     *   request = OpenSSL::OCSP::Request.new
+     *   request.add_certid certificate_id
+     *
+     * Adding a nonce to the request protects against replay attacks but not
+     * all CA process the nonce.
+     *
+     *   request.add_nonce
+     *
+     * To submit the request to the CA for verification we need to extract the
+     * OCSP URI from the subject certificate:
+     *
+     *   authority_info_access = subject.extensions.find do |extension|
+     *     extension.oid == 'authorityInfoAccess'
+     *   end
+     *
+     *   descriptions = authority_info_access.value.split "\n"
+     *   ocsp = descriptions.find do |description|
+     *     description.start_with? 'OCSP'
+     *   end
+     *
+     *   require 'uri'
+     *
+     *   ocsp_uri = URI ocsp[/URI:(.*)/, 1]
+     *
+     * To submit the request we'll POST the request to the OCSP URI (per RFC
+     * 2560).  Note that we only handle HTTP requests and don't handle any
+     * redirects in this example, so this is insufficient for serious use.
+     *
+     *   require 'net/http'
+     *
+     *   http_response =
+     *     Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http|
+     *       http.post ocsp_uri.path, request.to_der,
+     *                 'content-type' => 'application/ocsp-request'
+     *   end
+     *
+     *   response = OpenSSL::OCSP::Response.new http_response.body
+     *   response_basic = response.basic
+     *
+     * First we check if the response has a valid signature.  Without a valid
+     * signature we cannot trust it.  If you get a failure here you may be
+     * missing a system certificate store or may be missing the intermediate
+     * certificates.
+     *
+     *   store = OpenSSL::X509::Store.new
+     *   store.set_default_paths
+     *
+     *   unless response.verify [], store then
+     *     raise 'response is not signed by a trusted certificate'
+     *   end
+     *
+     * The response contains the status information (success/fail).  We can
+     * display the status as a string:
+     *
+     *   puts response.status_string #=> successful
+     *
+     * Next we need to know the response details to determine if the response
+     * matches our request.  First we check the nonce.  Again, not all CAs
+     * support a nonce.  See Request#check_nonce for the meanings of the
+     * return values.
+     *
+     *   p request.check_nonce basic_response #=> value from -1 to 3
+     *
+     * Then extract the status information from the basic response.  (You can
+     * check multiple certificates in a request, but for this example we only
+     * submitted one.)
+     *
+     *   response_certificate_id, status, reason, revocation_time,
+     *     this_update, next_update, extensions = basic_response.status
+     *
+     * Then check the various fields.
+     *
+     *   unless response_certificate_id == certificate_id then
+     *     raise 'certificate id mismatch'
+     *   end
+     *
+     *   now = Time.now
+     *
+     *   if this_update > now then
+     *     raise 'update date is in the future'
+     *   end
+     *
+     *   if now > next_update then
+     *     raise 'next update time has passed'
+     *   end
+     */
+
     mOCSP = rb_define_module_under(mOSSL, "OCSP");
 
+    /*
+     * OCSP error class.
+     */
+
     eOCSPError = rb_define_class_under(mOCSP, "OCSPError", eOSSLError);
 
+    /*
+     * An OpenSSL::OCSP::Request contains the certificate information for
+     * determining if a certificate has been revoked or not.  A Request can be
+     * created for a certificate or from a DER-encoded request created
+     * elsewhere.
+     */
+
     cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject);
     rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc);
     rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1);
@@ -711,6 +1037,11 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1037
     rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1);
     rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0);
 
+    /*
+     * An OpenSSL::OCSP::Response contains the status of a certificate check
+     * which is created from an OpenSSL::OCSP::Request.
+     */
+
     cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject);
     rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2);
     rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc);
@@ -720,6 +1051,12 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1051
     rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0);
     rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0);
 
+    /*
+     * An OpenSSL::OCSP::BasicResponse contains the status of a certificate
+     * check which is created from an OpenSSL::OCSP::Request.  A
+     * BasicResponse is more detailed than a Response.
+     */
+
     cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject);
     rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc);
     rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1);
@@ -730,6 +1067,11 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1067
     rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1);
     rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1);
 
+    /*
+     * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so
+     * that a status check can be performed.
+     */
+
     cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject);
     rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc);
     rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1);
@@ -739,43 +1081,107 @@ Init_ossl_ocsp() https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ocsp.c#L1081
 
 #define DefOCSPConst(x) rb_define_const(mOCSP, #x, INT2NUM(OCSP_##x))
 
-    DefOCSPConst(RESPONSE_STATUS_SUCCESSFUL);
-    DefOCSPConst(RESPONSE_STATUS_MALFORMEDREQUEST);
-    DefOCSPConst(RESPONSE_STATUS_INTERNALERROR);
-    DefOCSPConst(RESPONSE_STATUS_TRYLATER);
-    DefOCSPConst(RESPONSE_STATUS_SIGREQUIRED);
-    DefOCSPConst(RESPONSE_STATUS_UNAUTHORIZED);
-
-    DefOCSPConst(REVOKED_STATUS_NOSTATUS);
-    DefOCSPConst(REVOKED_STATUS_UNSPECIFIED);
-    DefOCSPConst(REVOKED_STATUS_KEYCOMPROMISE);
-    DefOCSPConst(REVOKED_STATUS_CACOMPROMISE);
-    DefOCSPConst(REVOKED_STATUS_AFFILIATIONCHANGED);
-    DefOCSPConst(REV (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]